了解 Archlinux

为什么使用 Linux?

简单来说,现在世界上流行的 PC 操作系统有三个,Windows,Linux 与 macOS。

如果你是计算机相关专业的学生或者从业者,非常建议你使用 Linux 作为自己的日常系统。在使用 Linux 系统的过程中,可以无形中接触到各个方面的计算机知识,并且在未来的工作中也会为你带来相关方面的优势。 macOS 在一些方面(即大致为 BSD 与 GNU/Linux 各方面的区别 )与 Linux 的操作并不同,并且由于其封闭的特性,我们不建议使用。Windows 在很多编程环境的配制过程中异常痛苦,且会出现各种各样的问题,强烈不建议使用 Windows 进行编程(除非你学的就是 Windows 系统编程)。

更重要的是,GNU/Linux 是自由软件运动的相关重要产物。自由软件运动(free software movement)拒绝专有软件并推广自由软件,它的终极目标在于解放网络世界中的每个人——即每个电脑用户。每个人都应拥有完全掌控所运行软件的权利。自由软件有如下四项原则:

  • 自由度 0:无论用户出于何种目的,必须可以按照用户意愿,自由地运行该软件。
  • 自由度 1:用户可以自由地学习并修改该软件,以此来帮助用户完成用户自己的计算。作为前提,用户必须可以访问到该软件的源代码。
  • 自由度 2:用户可以自由地分发该软件的拷贝,这样就可以助人。
  • 自由度 3:用户可以自由地分发该软件修改后的拷贝。借此,用户可以把改进后的软件分享给整个社区令他人也从中受益。作为前提,用户必须可以访问到该软件的源代码。

如果你只是一个普通用户,你一定见识过没有经过你的授权,电脑被装上了成堆的流氓软件的类似经历。专有软件不仅在各个维度强奸着用户,更包含着难以想象的恶意功能。用户的数据,隐私等重要信息会轻而易举被大公司们收集走,并加以滥用,这成为业内公开的秘密已是不争的事实。在专用软件有同类的自由软件替代时,强烈建议你迁移至自由软件。本书会同时记录专有软件与自由软件,因为如果完全摒弃专有软件,一定会直接将很多人阻挡在 linux 之外,这不是我们所希望的,我们希望先将更多人接纳到 GNU/Linux 中,至少这是踏出的第一步。但这并不代表我们支持使用专有软件,我们希望你至少可以先踏入 linux,逐渐使用自由软件替代专有软件。专有软件在本书中仅作简要记录,不会详细描述,因为我们不希望你长期依赖于它。只要某个专有软件有足够优秀的自由软件替代品出现,我们就会移除本教程中的那个专有软件。专有软件在本书中会被角标专有或描述额外标记。如果你是有能力的开发者,更希望你可以开发出替代某些专有软件的自由软件。

最后,如果你想尝试完全免费的系统,或是喜欢探索充满新鲜与挑战的事物,Linux 也是你不可错过的体验。

为什么使用 Arch Linux?

Archlinux 的许多特点如同双刃剑,既是优点,也是缺点

简洁

Archlinux 将简洁定义为:避免任何不必要的添加、修改和复杂增加。简单来说,Archlinux 是一个可以让用户自己动手打造的操作系统。从安装到管理,Archlinux 放手让用户处理一切。

用户可以自己决定使用哪种桌面环境、安装哪些组件和服务。这种精细化的控制能够赋予你一个精简的操作系统,可以让用户自由选择所需的组件来构建属于用户自己的系统。

但也正因为此配置 Archlinux 相对于其它 Linux 发行版来说是繁琐。但繁琐是自由的代价。如果你是一个 DIY 爱好者,那么相信你会爱上 Archlinux 的 ♥。

滚动更新(现代)

滚动更新(rolling update)是指软件开发中经常性将更新发送到软件的概念。相较于滚动发行,有标准版本和小数点版本的版本号开发模式,必需通过重新安装以取代先前的发行版。Archlinux 是没有版本概念的,它始终保持最新的状态,通俗的理解就相当于把发行版比喻为一部车,ubuntu 更新就是换一部新的,而 Archlinux 就是把车里面旧的配件换成新的。

Archlinux 是一个滚动发行版,这意味着:

  1. 新的内核和应用程序版本一经发布,就会立即向用户推送
  2. 当大多数其它 Linux 发行版还在提供旧的 Linux 内核版本时,Archlinux 会迅速向用户提供最新的内核
  3. 而软件也是如此。如果 Archlinux 仓库中的软件发布了新版本,Archlinux 用户通常会比其他用户先获得新版本
  4. 在滚动发行模式下,一切都是新鲜和前沿的。用户不必把操作系统从一个版本升级到另一个版本,只要使用 pacman 的升级命令,便会始终保持最新的版本

实用

Archlinux 注重实用性,避免意识形态之争。最终的设计决策都是由开发者的共识决定。开发者依赖基于事实的技术分析和讨论,避免政治因素,不会被流行观点左右。

Archlinux 的仓库中包含大量的软件包和编译脚本。用户可以按照需要自由选择。仓库中既提供了开源、自由的软件,也提供了闭源软件(大部分闭源软件在 AUR 仓库中)。实用性大于意识形态

以用户为中心

许多 Linux 发行版都试图变得更“用户友好”,Archlinux 则一直是且永远会是“以用户为中心”。Archlinux 是为了满足贡献者的需求,而不是为了吸引尽可能多的用户。Archlinux 适用于乐于自己动手的用户,他们愿意花时间阅读文档,解决自己的问题。

Archlinux 鼓励每一个用户 参与 和贡献,报告和帮助修复 bugs,提供软件包补丁和参加核心 项目 —— Archlinux 开发者都是志愿者,通过持续的贡献成为团队的一员。

Archers 可以自行贡献软件包到 Arch 用户仓库AUR);提升 archWiki 文档质量;在 论坛邮件列表IRC 中给其他用户提供技术支持. Archlinux 是全球很多用户的选择,已经有很多 国际社区 提供帮助和文档翻译。

同样的,若希望为本指南做出贡献,以帮助更多的人,请参阅 贡献指南

Arch 用户仓库(AUR)

AUR 即 Arch 用户仓库(Arch User Repository)。它包含名为 PKGBUILD 的包描述,它可让用户使用 makepkg 从源代码编译软件包,然后通过 pacman 安装。

创建 AUR 的目的是组织和共享社区中的新软件包,并帮助加速将流行的软件包纳入社区仓库。进入官方仓库的大量新软件包都从 AUR 开始。在 AUR 中,用户可以贡献自己的软件包构建(PKGBUILD 和相关文件)。AUR 社区可以对 AUR 中的软件包进行投票。如果一个软件包变得足够流行(且具有兼容的许可证和良好的打包技术),那么可以将其加入 pacman 直接访问的社区仓库中。

激进的内核更新机制

Archlinux 在更新内核的时候会立即删除旧内核(因为内核也是一个软件包 linux / linux-zen…,由 pacman 更新)

立即删除旧的内核要求 Archlinux 必须重启来加载新的内核,否则容易发生诡异的问题。这是因为 Linux 所谓的“内核”包含有大量的动态加载模块,如果在某次启动后,某个模块没有被加载过,然后系统内核更新了并且删除了旧的内核,那么这些模块将永远不能被加载了——因为它们随着旧内核被删掉了。除非用户重启系统以完整切换到新的内核以使用新版的动态加载模块。

笔者曾经就因为在升级内核后插上声卡无法工作而感到困惑,后来才意识到问题所在。所以建议在更新内核后重新启动系统以避免问题的产生。(win10 更新也要重启,对吧?)

软件包管理体系

不同于 Debian 系列的 apt / dpkg 和 Red Hat 系列的 dnf(yum)/ rpm 包管理体系,Archlinux 只用了一个工具 pacman 就解决了获取和安装两个功能。这降低了为 Archlinux 制作软件包的门槛,这也是 AUR 几乎能涵盖整个 Linux 软件生态的主要原因。但是这也导致 pacman 不支持虚包(virtual package),虚包是一个通用名称,适用于一组提供类似的基本功能的包中的任何一个包。

由社区创建、支持和拥有

Ubuntu 由 Canonical 支持,Fedora 来自 Red Hat(现在是 IBM 的一部分),openSUSE 来自 SUSE。这些主流发行版都是企业支持的。

这本身并不是坏事或过错,但是有一些人不喜欢企业参与开源项目。

正如 Debian 一样,Archlinux 是为数不多的仅由社区创建、支持和拥有的的 Linux 发行项目之一。

安装前的准备

Arch Linux 能运行在最少 512 MiB 内存的 x86_64 机器上,但从安装媒介启动系统并成功安装需要更多的内存。[1] 基本安装将占用小于 2 GiB 的存储空间。由于安装过程中需要从远程存储库获取软件包,计算机将需要一个有效的互联网连接。

由于当前 UEFI 已普及十余年,安装将全部以 UEFI+GPT 的形式进行,传统 BIOS 方式不再赘述。

确保网络环境

如果你可以使用路由器分接出来的网线,以 dhcp 的方式直接上网,那么不用准备什么。如果你的环境只能使用无线网络安装,需要事先把自己所用的 wifi 名称改成自己能记住的英文名称。因为安装时无法显示和输入中文名的 wifi,你会看到一堆不知道是什么的方块,并且在安装过程中你将没有办法输入中文的无线名称进行连接。虽然通过一些繁琐的步骤可以解决终端中文的问题,但是显然这么做在安装 Arch Linux 时毫无必要。

其次,有些笔记本电脑上存在无线网卡的硬件开关或者键盘控制,开机后安装前需要确保你的无线网卡硬件开关处于打开状态

刻录启动优盘

准备一个 2G 以上的优盘,刻录一个安装启动盘。安装镜像 iso 在下载页面下载,你需要选择通过磁力链接或 torrent 下载,下载完成后,还需要在 Archlinux 下载页面下载PGP signature签名文件(不要从镜像源下载签名文件),将签名文件和 iso 镜像置于同一文件夹,随后进行对镜像的签名校验,以保证下载的镜像是完整,无错误的,未被篡改的。在一台已经安装 GnuPG 的系统上,执行以下命令,确保输出完好的签名。具体镜像名根据名字自行修改。

gpg --keyserver-options auto-key-retrieve --verify Archlinux-202x.0x.01-x86_64.iso.sig

注意,这里的签名校验非常重要,这可以保证你的安装镜像是未被篡改的,同时可以保证你在使用安装盘安装系统时,用正确的公钥校验安装包。

另外,在一台已经安装 Arch Linux 的计算机上可以通过以下方式验证:

pacman-key -v Archlinux-version-x86_64.iso.sig

Windows 下推荐使用ventoy或者Rufus或者etcher进行优盘刻录。三者皆为自由软件。具体操作请自行查阅,都非常简单。

Linux 下可以直接用 dd 命令进行刻录。注意 of 的参数为 sdx,不是 sdx1 sdx2 等。

sudo dd bs=4M if=/path/to/Archlinux.iso of=/dev/sdx status=progress oflag=sync

bs=4M 指定一个较为合理的文件输入输出块大小。 status=progress 用来输出刻录过程总的信息。 oflag=sync 用来控制写入数据时的行为特征。确保命令结束时数据及元数据真正写入磁盘,而不是刚写入缓存就返回。

进入主板 BIOS 进行设置

插入优盘并开机。在开机的时候,按下 F2/F8/F10/DEL 等(取决与你的主板型号,具体请查阅你主板的相关信息)按键,进入主板的 BIOS 设置界面。

关闭主板设置中的 Secure Boot

Arch Linux 安装镜像不支持安全启动(Secure Boot)。要引导安装媒介,需要禁用安全启动。在类似名为 security 的选项卡中,找到一项名为 Secure Boot(名称可能略有差异)的选项,选择 Disable 将其禁用。如果需要,可在完成安装后重新配置 安全启动

具体怎么关因为每种电脑的方法不一样于是汝要自己 STFW (Search the f**king Web,搜索一下) 了

调整启动方式为 UEFI

在某些旧的主板里,需要调整启动模式为 UEFI,而非传统的 BIOS/CSM。在类似名为 boot 的选项卡中,找到类似名为 Boot Mode 的选项,确保将其调整为 UEFI only,而非 legacy/CSM。

调整硬盘启动顺序

在类似名为 boot 的选项卡中,找到类似名为 Boot Options(名称可能略有差异)的设置选项,将 USB 优盘的启动顺序调至首位。

准备安装

最后保存 BIOS 设置并退出,一般的按键是 F10。此时系统重启,不出意外你应该已经进入 Archlinux 的安装界面。

基础安装

本节从安装最基础的无图形化 Archlinux 系统开始。官方安装指南

如果想一边安装,一边使用 Lynx 查看本指南,可以使用 Alt+箭头 快捷键切换不同的控制台。

键盘布局

控制台键盘布局默认为 us(美式键盘映射)。列出所有可用的键盘布局,可以使用:

# ls /usr/share/kbd/keymaps/**/*.map.gz

如果您想要更改键盘布局,可以将一致的文件名添加进 loadkeys(1),但请省略路径和扩展名。比如,要添加 德语 键盘布局:

# loadkeys de-latin1

禁用 reflector

2020 年,Archlinux 安装镜像中加入了 reflector 服务,它会自己更新 mirrorlist(软件包管理器 pacman 的软件源)。在特定情况下,它会误删某些有用的源信息。这里进入安装环境后的第一件事就是将其禁用。也许它是一个好用的工具,但是很明显,因为地理上造成的特殊网络环境,这项服务并不适合启用。

# systemctl stop reflector.service

再次确保是否为 UEFI 模式

在一系列的信息刷屏后,可以看到已经以 root 登陆安装系统了,请用下列命令列出 efivars 目录:

# ls /sys/firmware/efi/efivars

如果命令结果显示了目录且没有报告错误,则系统以 UEFI 模式引导。 如果目录不存在,则系统可能以 BIOS 模式 (或 CSM 模式) 引导。

连接网络

一般来说,你连接的网络几乎均可以通过 DHCP 的方式来进行 IP 地址和 DNS 的相关设置,你无需进行额外操作。在没有合适网络的情况下,使用手机的移动热点也是很方便的选择。如果你的网络环境需要配置静态 IP 和 DNS,请自行参考 Arch Wiki。

对于有线连接来说,直接插入网线即可。

对于无线连接,则需进行如下操作进行网络连接。

无线连接使用 iwctl 命令进行,按照如下步骤进行网络连接:

iwctl                           #执行iwctl命令,进入交互式命令行
device list                     #列出设备名,比如无线网卡看到叫 wlan0
station wlan0 scan              #扫描网络
station wlan0 get-networks      #列出网络 比如想连接YOUR-WIRELESS-NAME这个无线
station wlan0 connect YOUR-WIRELESS-NAME #进行连接 输入密码即可
exit                            #成功后exit退出

可以等待几秒等网络建立链接后再进行下面测试网络的操作。

ping www.gnu.org

如果你不能正常连接网络,首先确认系统已经启用网络接口[1]

ip link              #列出网络接口信息,如不能联网的设备叫wlan0
ip link set wlan0 up #比如无线网卡看到叫 wlan0

如果随后看到类似Operation not possible due to RF-kill的报错,继续尝试rfkill命令来解锁无线网卡。

rfkill unblock wifi

如果汝明明有无线网卡却没识别的话,有可能汝是某无线网卡厂商受害者😂😂。这时可以:

  • 有 Android 手机的话,手机连 WiFi ,然后用“USB 网络共享”共享给电脑。
  • 找个 USB 无线网卡插上 😂
  • 连有线

移动宽带调制解调器(移动网卡) - 使用 mmcli 实用程序连接到移动网络。


另外,如果汝连接的网络需要网页登录(Captive Portal),可以用 elinks 碰碰运气 😂

# elinks http://<your_captive_portal_url>
  • 用汝的门户的 URL 替换 <your_captive_portal_url>
  • 如果不知道的话,随便访问一个 HTTP 网站试试,应该就会被重定向到 Portal 了

更新系统时间

使用 timedatectl(1) 确保系统时间是准确的:用 timedatectl set-ntp true 保证时间同步 。

timedatectl set-ntp true    #将系统时间与网络时间进行同步
timedatectl status          #检查服务状态

因为有不少操作需要准确的时间呐,例如 HTTPS 和 GnuPG 都需要准确的时间来验证证书的有效性。

但是如果因为各种原因没法同步的话,那就只好手动设置咯~

# timectl set-time "yyyy-MM-dd hh:mm:ss"
# timectl set-time "2016-10-28 17:39:42"

分区

系统如果识别到磁盘,就会将其分配为一个块设备,如 /dev/sda/dev/nvme0n1/dev/mmcblk0。可以使用 lsblk 或者 fdisk -l 查看。结果中以 romloop 或者 airoot 结尾的设备可以被忽略。

对于一个选定的设备,以下分区是必须要有的:

如果需要创建多级存储例如 LVMdisk encryptionRAID,请在此时完成。

使用 Ext4

这里总共设置三个分区,是一个我们认为较为通用的方案。此步骤会清除磁盘中全部内容,请事先确认。

  • EFI 分区[2]/boot/efi 至少 300 MiB
  • Linux swap (交换空间): 大于 512 MiB
  • Linux x86-64 根目录 : / 剩余空间

这里根目录的大小仅为参考,一般来说个人日常使用的 linux 分配 100G 已经够用了。根目录最小建议不小于 50G,根目录过小会造成无法更新系统软件包等问题。

首先将磁盘转换为 gpt 类型,这里假设比如你想安装的磁盘名称为 sdx。如果你使用 NVME 的固态硬盘,你看到的磁盘名称可能为 nvme0n1。

lsblk                       #显示分区情况 找到你想安装的磁盘名称
parted /dev/sda             #执行parted,进入交互式命令行,进行磁盘类型变更
(parted)mktable             #输入mktable
New disk label type? gpt    #输入gpt 将磁盘类型转换为gpt 如磁盘有数据会警告,输入yes即可
quit                        #最后quit退出parted命令行交互

接下来使用 cfdisk 命令对磁盘分区。进入 cfdisk 后的操作很直观,用键盘即可操作分配各个分区的大小与格式。一般建议将 EFI 分区设置为磁盘的第一个分区,据说有些主板如果不将 EFI 设置为第一个分区,可能有不兼容的问题。其中 EFI 分区选择EFI System类型,其余两个分区选择Linux filesystem类型。

cfdisk /dev/sda #来执行分区操作,分配各个分区大小,类型
fdisk -l        #分区结束后, 复查磁盘情况

格式化

建立好分区后,需要对分区用合适的文件系统进行格式化。这里用mkfs.ext4命令格式化根分区与 home 分区,用mkfs.vfat命令格式化 EFI 分区。如下命令中的 sdax 中,x 代表分区的序号。格式化命令要与上一步分区中生成的分区名字对应才可以。

磁盘若事先有数据,会提示你: ‘proceed any way?’ 按 y 回车继续即可。

mkfs.ext4 -L ROOT  /dev/sda3     #格式化根目录和home目录的两个分区
mkfs.fat -F 32 -n EFI /dev/sda1 #格式化 EFI 分区

如果创建了 交换分区,请使用 mkswap(8) 将其初始化:

mkswap -L SWAP /dev/sda2 #交换空间分区

挂载

首先还是用 lsblk 确定一下分区的名称,为了以防万一记得加上 -f 参数

# lsblk -f

在挂载时,挂载是有顺序的,先挂载根分区,再挂载 EFI 分区。 这里的 sdax 只是例子,具体根据你的实际情况来。

mkdir -p /mnt/arch
mount /dev/sda3  /mnt/arch
mkdir -p /mnt/arch/boot/efi
mount /dev/sda1 /mnt/arch/boot/efi

如果创建了 swap 交换空间卷,请使用 swapon(8) 启用它:

swapon /dev/sda2 #交换空间分区

使用 Btrfs

纵观 Btrfs 的历史,可以说 Btrfs 未来的发展是道阻且长的。也让我们感受到开源社区也并不是一根绳上的蚂蚱 —— 开源社区之间也有着各种各样的分歧。

但不管怎么说,Btrfs 的未来现在来看是光明的;我们也可以在 archlinux 上享受到 Btrfs 文件系统的特性带来的好处:

  1. 快照 —— archlinux 作为滚动发行版,若滚挂了可以使用 Btrfs 的快照特性快速回滚
    • 若使用传统的 ext4 文件系统,我们可以使用 timeshiftRSYNC 模式进行增量备份。但是,一般来说 RSYNC 方式的快照大小略大于当前实际使用大小,也就是说实际上开启了 timeshiftRSYNC 模式快照相当于磁盘可用空间直接少了一半多。因为虽然 RSYNC 方式的快照是增量的,但历史最久远的快照依然是完整备份,随后才是增量的
  2. 透明压缩 —— 可以大大减少磁盘的使用空间(压缩率大概在 10% 左右)

首先我们需要将整一个分区格式化为 Btrfs 文件系统。使用如下命令进行格式化:

# mkfs.btrfs -L myArch /dev/sda3

-L 选项后指定该分区的 LABLE,这里以 myArch 为例,也可以自定义,但不能使用特殊字符以及空格,且最好有意义

为了创建子卷,我们需要先将 Btrfs 分区挂载到 /mnt

# mount -t btrfs -o compress=zstd /dev/sda3 /mnt/arch
  • -t 选项后指定挂载分区文件系统类型
  • -o选项后添加挂载参数:
    • compress=zstd —— 开启透明压缩

通过以下命令创建两个 Btrfs 子卷,之后将分别挂载到 / 根目录和 /home 用户主目录:

btrfs subvolume create /mnt/arch/@     #创建 / 目录子卷
btrfs subvolume create /mnt/arch/@home #创建 /home 目录子卷

通过以下命令复查子卷情况:

# btrfs subvolume list -p /mnt/arch

子卷创建好后,我们需要将 /mnt 卸载掉,以挂载子卷:

# umount /mnt/arch

在挂载时,挂载是有顺序的,需要从根目录开始挂载。使用如下命令挂载子卷:

# mount -t btrfs -o subvol=/@,compress=zstd /dev/sda3 /mnt/arch
# mkdir /mnt/arch/home
# mount -t btrfs -o subvol=/@home,compress=zstd /dev/sda3 /mnt/arch/home
# mkdir -p /mnt/arch/boot/efi
# mount /dev/sda1 /mnt/arch/boot/efi
# swapon /dev/sda2

镜像源的选择

文件 /etc/pacman.d/mirrorlist 定义了软件包会从哪个镜像源下载。使用如下命令编辑镜像列表:

# mv /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
# cat > /etc/pacman.d/mirrorlist

在列表中越前的镜像在下载软件包时有越高的优先权。添加北外、中科大或者清华的放在最上面即可。

Server = https://mirrors.bfsu.edu.cn/archlinux/$repo/os/$arch
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch

如果其速度不佳,可以手动指定其他镜像源。完整的镜像源列表可参考官方镜像源生成器

然后用 pacman -Syy 刷新一下软件包数据库。

# pacman -Syy

安装系统

必须的基础包

# pacstrap /mnt/arch base base-devel linux linux-headers linux-firmware
  • base 元包:包含基本系统所需的依赖,如果汝在别的地方见到说 base 是个软件包组的说法,忘了它吧……
  • 要通过 AUR 或者 ABS 编译安装软件包,还需要安装 base-devel 啦 (现在它还是个软件包组)。
  • 然后大多数情况下,汝还需要一个内核。目前官方仓库里有这些
  • 对于大多数情况下,汝可能会考虑安装固件包 linux-firmware 。

必须的功能性软件

# pacstrap /mnt/arch dhcpcd iwd vim e2fsprogs dosfstools bash-completion man-db
  • dhcpcd 不论有限还是无限网络都需要

  • 要通过刚刚用过的 iwctl 连接无线网络的话,记得安装 iwd 。

  • 一个文字编辑器,例如刚才用到的 nanovim 。建立自己的 .vimrc

    cp /usr/share/vim/vim82/defaults.vim .vimrc
    
  • 一些文件系统工具,例如 操作 ext4 的 e2fsprogs,操作 FAT/FAT32 的 dosfstools, 需要读写其它文件系统的话,可以在 https://wiki.Archlinux.org/index.php/File_system 找到相应的用户空间工具。

  • 访问 maninfo 页面中文档的工具:man-dbman-pagestexinfo

诶,base 组不包含 live 环境中所有的软件包么?是的, https://projects.Archlinux.org/archiso.git/tree/configs/releng/packages.x86_64 列出了在 ISO 中的 live 环境中安装的但不在 base 包中的软件包。


可选,安装 openssh

# pacstrap /mnt/arch openssh
# cat >> /mnt/arch/etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes

生成 fstab 文件

fstab 用来定义磁盘分区

  • use UUID

    # genfstab -U /mnt/arch > /mnt/arch/etc/fstab
    
  • use Label

    # genfstab -L /mnt/arch > /mnt/arch/etc/fstab
    

复查一下 /mnt/etc/fstab 确保没有错误

# cat /mnt/arch/etc/fstab

change root

把环境切换到新系统的/mnt 下

# arch-chroot /mnt/arch

时区设置

设置时区,在/etc/localtime 下用/usr 中合适的时区创建符号连接。如下设置上海时区。

# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

随后进行硬件时间设置,将当前的正确 UTC 时间写入硬件时间。

# hwclock --systohc

这个命令假定已设置硬件时间为 UTC 时间,并调整时间漂移:。详细信息请查看 System time#Time standard

如果汝正在尝试安装双系统,在进入 Windows 以后可能会发现 Windows 的时间不对了 ,因为 Windows 默认的硬件时钟是 localtime(

可以用一条注册表键值让 Windows 使用 UTC 作为硬件时钟(在早于 Windows 7 的系统上发现过这样做会出现一些严重的问题: http://www.cl.cam.ac.uk/~mgk25/mswish/ut-rtc.html

reg add "HKEY_LOCAL_MACHINESystemCurrentControlSetControlTimeZoneInformation" /v RealTimeIsUniversal /d 1 /t REG_DWORD /f

设置 Locale 进行本地化

Locale 决定了地域、货币、时区日期的格式、字符排列方式和其他本地化标准。

编辑 /etc/locale.gen,在最后添加:

# cat >> /etc/locale.gen
en_US.UTF-8 UTF-8
zh_CN.UTF-8 UTF-8

然后使用如下命令生成 locale。

# locale-gen

向 /etc/locale.conf 导入内容

# echo 'LANG=en_US.UTF-8'  >> /etc/locale.conf

如果需要修改 #键盘布局,可编辑 vconsole.conf(5) 使其长期生效,例如:

# vim /etc/vconsole.conf
KEYMAP=de-latin1

设置主机名

首先在/etc/hostname设置主机名

# vim /etc/hostname
myarch

加入你想为主机取的主机名,这里比如叫 myarch。

接下来在/etc/hosts设置与其匹配的条目。

# vim /etc/hosts

加入如下内容

127.0.0.1   localhost
::1         localhost
127.0.1.1   myarch.localdomain myarch

某些情况下如不设置主机名,在 KDE 下可能会存在网络情况变更时无法启动 GUI 应用的问题,在终端中出现形如No protocol specified qt.qpa.xcb: could not connect to display的错误,这种情况较为少见[[3]]https://bbs.Archlinux.org/viewtopic.php?id=241338]https://bbs.Archlinux.org/viewtopic.php?id=243674](https://wiki.Archlinux.org/title/Network_configuration#Local_hostname_resolution)。

Initramfs

Creating a new initramfs is usually not required, because mkinitcpio was run on installation of the kernel package with pacstrap.

For LVM, system encryption or RAID, modify mkinitcpio.conf(5) and recreate the initramfs image:

# mkinitcpio -P

具体例子查看 dm-crypt/Encrypting an entire system

为 root 用户设置密码

# passwd root

安装微码

pacman -S intel-ucode   #Intel
pacman -S amd-ucode     #AMD

安装引导程序

# pacman -S grub efibootmgr
# pacman -S os-prober
# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB
  • rub是启动引导器,efibootmgr被 grub 脚本用来将启动项写入 NVRAM。
  • 如果汝的硬盘上没有其它系统,那么可以不用装 os-prober
  • --efi-directory=/efi —— 将 grubx64.efi 安装到之前的指定位置(EFI 分区)
  • --bootloader-id=GRUB —— 取名为 GRUB

接下来编辑/etc/default/grub 文件,去掉GRUB_CMDLINE_LINUX_DEFAULT一行中最后的 quiet 参数,同时把 log level 的数值从 3 改成 5。这样是为了后续如果出现系统错误,方便排错。同时在同一行加入 nowatchdog 参数,这可以显著提高开关机速度。

# vim /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="loglevel=5 nowatchdog"

最后生成 GRUB 所需的配置文件

# grub-mkconfig -o /boot/grub/grub.cfg

我们在之前的命令中指定了 bootloader-id 为 GRUB,这一般不会出现问题。然而在某些主板安装完成后,你会发现没有 nvme 启动条目。这是因为某些主板的 UEFI 固件在显示 UEFI NVRAM 引导条目之前,需要在特定的位置存放可引导文件,不支持自定义存放 efi 文件[6]。解决方式是使用--removable 参数解决一些主板 NVRAM 的兼容性问题(包括虚拟机)。

# grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --removable
# grub-mkconfig -o /boot/grub/grub.cfg

除此之外,如果你的主板是一些较老的型号,如 intel 9 系列以下或者较老 AMD 的主板,它们很可能不支持从 nvme 启动系统,虽然可以通过修改 BIOS 加入 NVME 支持模块来解决,但这不在本文讨论范围内。


为了引导 win10,则还需要添加新的一行 GRUB_DISABLE_OS_PROBER=false

# GRUB boot loader configuration
...
GRUB_DISABLE_OS_PROBER=false

如果汝安装了 os-prober 然后看到这样的警告:

WARNING: Failed to connect to lvmetad. Falling back to device scanning.

这是因为在 chroot 环境里面 /run 是不可用的。只要每个步骤都做对了,这些警告不会影响系统启动, 汝可以放心继续进行下一步的系统安装咯。 如果汝重启之后没看到 Windows 的启动项,试着进入 Arch 之后再运行那条生成配置文件的命令。

完成安装

exit                     # 退回安装环境#
umount -R  /mnt/arch     # 卸载新分区
reboot                   # 重启

注意,重启前要先拔掉优盘,否则你重启后还是进安装程序而不是安装好的系统。重启后,开启 dhcp 服务,即可连接网络

systemctl enable --now dhcpcd  #立即启动dhcp
ping www.gnu.org        #测试网络连接

若为无线链接,则还需要启动 iwd 才可以使用 iwctl 连接网络

systemctl enable --now iwd #立即启动iwd
iwctl                      #和之前的方式一样,连接无线网络

到此为止,一个基础的,无 UI 界面的 Arch Linux 已经安装完成了。紧接着下一节,我们来安装图形界面。

桌面环境与常用应用

官方文档: 安装后的工作 本节只介绍最基本的,能使系统真正意义上可用所需的组件

注: 文档中带有 AUR 角标的软件代表是用户自行打包的第三方软件AUR,不在 Arch 官方支持范围内,可能会出现更新不及时、无法安装、使用出错等各种问题。如果遇到问题,你可自行去其 AUR 页面查看其他人的评论中是否有解决方法。如果不是实在没有官方支持的同类软件,则不建议使用。

确保系统为最新

如果你在做完上一节的内容后,重启并放置过一段时间,那需要先按照上节末尾处的方式重新连接网络,然后更新系统。

pacman -Syyu    #升级系统中全部包

准备非 root 用户

添加用户,比如新增加的用户叫 testuser

# useradd -m -G wheel -s /bin/bash kurome
  • wheel附加组可sudo,以root用户执行命令
  • -m同时创建用户家目录

设置新用户 testuser 的密码

# passwd kurome

编辑 sudoers 配置文件

# pacman -S sudo
# EDITOR=vim visudo

找到下面这样的一行,把前面的注释符号 # 去掉,:wq 保存并退出即可。

#%wheel ALL=(ALL) ALL

这里稍微解释一下:

  • %wheel —— 用户名或用户组,此处则代表是 wheel 组,% 是用户组的前缀
  • ALL= —— 主机名,此处则代表在所有主机上都生效(如果把同样的 sudoers 文件下发到了多个主机上)
  • (ALL) —— 用户名,此处则代表可以成为任意目标用户
  • 最后的 ALL —— 代表可以执行任意命令

几个更详细的例子:

mailadmin 组里的用户可以作为 root 用户,在 snowrain 这两台主机执行一些邮件服务器控制命令(命令之间用 , 分隔):

%mailadmin  snow,rain=(root)  /usr/sbin/postfix, /usr/sbin/postsuper, /usr/bin/doveadm

用户 whoami 可以在所有主机上以 root 用户不输入密码执行 rndc reload 这条命令(正常来说 sudo 都是要求输入调用方的密码的):

whoami  ALL=(root)  NOPASSWD: /usr/sbin/rndc reload

当在 users 组里的用户以 sudo passwd 或者 sudo passwd root 方式运行命令的时候,可以直接把 root 用户的密> 码 改掉,这真是太危险了!必须要把这两条命令禁止掉,但我们又希望用户可以通过 sudo passwd 修改其它用户的密码。那么我们可以在命令前面加上 ! 来表示不可执行的命令:

%users  ALL=(root)  !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd root

总结一下,语法如下:

用户名/%用户组名 主机名=(目标用户名) 命令1, 命令2, !命令3

安装桌面环境

安装桌面环境需要的基础包 (就是 xorg 啦)

# pacman -S xorg

这时会让汝选择需要哪些软件包啦,其实大多数时候默认的就行……

接下来挑一个喜欢的桌面环境包组装上咯~。咱这里就只举例 GNOME 、KDE 和 xfce 啦,其他官方支持的桌面环境可以去 https://wiki.Archlinux.org/index.php/Desktop_environment 查看

  • GNOME , 想要 GNOME 全家桶的话带上 gnome-extras

    # pacman -S gnome
    
  • KDE Plasma , 想要 KDE 全家桶的话用 kde-applications-meta 代替 。 kde-applications 会提示汝选择要安装哪些包。以及一个显示管理器, KDE 和 sddm 一起使用最好。

    # pacman -S plasma sddm kde-applications
    

    或者只安装一些基本组件(例如文件管理器和终端模拟器)。

    # pacman -S plasma sddm kde-applications-meta
    
  • xfce4,xfce 不带显示管理器,所以要装个其他的(例如 lightdm,还要装一个 greeter)

    # pacman -S xfce4 xfce4-goodies lightdm lightdm-gtk-greeter
    

    桌面环境大多数使用 NetworkManager ,xfce 的话,记得安装 network-manager-applet, 一个控制 NetworkManager 的小工具:

    # pacman -S network-manager-applet
    

配置 greeter sddm

# systemctl enable sddm

设置交换文件 swap(可选)

在桌面环境中,交换分区或文件用来实现休眠(hibernate)的功能,即将当前环境保存在磁盘的交换文件或分区部分。除此之外,某些特定软件需要 swap 才可以正确运行。交换文件与分区性能相同,且交换文件更为灵活,可随时变更大小,增加与删除。[1]

fallocate -l 4G /swapfile #创建4G的交换空间 大小根据需要自定
chmod 600 /swapfile       #设置正确的权限
mkswap /swapfile          #格式化swap文件
swapon /swapfile          #启用swap文件

最后,向/etc/fstab 中追加如下内容:

/swapfile none swap sw 0 0

KDE 自身提供开箱即用的睡眠功能(suspend),即将系统挂起到内存,消耗少量的电量。休眠(hibernate)会将系统挂起到交换分区或文件,几乎不消耗电量。睡眠功能已可满足绝大多数人的需求,如果你一定需要休眠功能,可以参考官方文档设置休眠相关步骤。

开启 32 位支持库

# vim /etc/pacman.conf

去掉[multilib]一节中两行的注释,来开启 32 位库支持。

[multilib]
Include = /etc/pacman.d/mirrorlist

最后:wq 保存退出,刷新 pacman 数据库

# pacman -Syyu

重启电脑,即可看到欢迎界面,输入新用户的密码即可登录桌面

添加中文社区仓库

/etc/pacman.conf 结尾处加入下面的文字,来添加 archlinuxcn 源。推荐的镜像源(选一个即可)也一并列出:

$ vim /etc/pacman.conf
[archlinuxcn]
# 北京外国语大学
Server = https://mirrors.bfsu.edu.cn/archlinuxcn/$arch
# 清华大学开源软件镜像站
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch 
# 中国科学技术大学开源镜像站
Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch 
# 哈尔滨工业大学开源镜像站
Server = https://mirrors.hit.edu.cn/archlinuxcn/$arch 
# 华为开源镜像站
Server = https://repo.huaweicloud.com/archlinuxcn/$arch 
$ sudo pacman -Sy
$ sudo pacman -S archlinuxcn-keyring

通过以下命令刷新 pacman 数据库并更新:

pacman -Syyu

安装基础功能包

进入桌面后,搜索 konsole。它是 KDE 桌面环境默认的命令行终端。

首先先进行桌面环境中的网络设置:

# 确保iwd开机处于关闭状态,其无线连接会与NetworkManager冲突
$ sudo systemctl disable iwd 
# 同上,立即关闭iwd
$ sudo systemctl stop iwd
# 确保先启动NetworkManager,并进行网络连接 若iwd已经与NetworkManager冲突 则执行完上一步重启一下电脑即可。
$ sudo systemctl enable --now NetworkManager                                 

接下来安装一些基础功能包。

# 识别NTFS格式的硬盘
$ sudo pacman -S ntfs-3g 
# 安装几个开源中文字体 一般装上文泉驿就能解决大多wine应用中文方块的问题
$ sudo pacman -S adobe-source-han-serif-cn-fonts wqy-zenhei
# 安装谷歌开源字体及表情
$ sudo pacman -S noto-fonts-cjk noto-fonts-emoji noto-fonts-extra
# 安装常用的火狐、谷歌浏览器
$ sudo pacman -S firefox chromium  
# 与dolphin同用右键解压
$ sudo pacman -S ark    
# 安装ark可选依赖
$ sudo pacman -S p7zip unrar unarchiver lzop lrzip 
# 确保Discover(软件中心)可用 需重启
$ sudo pacman -S packagekit-qt5 packagekit appstream-qt appstream  
# 图片查看器
$ sudo pacman -S gwenview  
# 一些工具
$ sudo pacman -S git wget kate bind                                          

不要安装过多字体:在字体超过 255 种时,某些 QT 程序可能无法正确显示某些表情和符号,详见链接2

设置 DNS

一般来说,如今大多电脑连接的路由器是可以自动处理 DNS 的,如果你的路由器不能处理,则需要额外进行 DNS 的设置。同时,如果使用 ISP 提供的默认 DNS,你的网络访问记录将存在更大的,被泄露或被当局存储记录的风险。除此之外,使用 ISP 提供的 DNS 还有可能将某些服务解析至一些已经失效或劣化的服务器。即使你的网络环境可以自动处理 DNS 设置,我们还是建议你使用可信的国际通用 DNS 设置。如下的配置将固定使用谷歌的 DNS,但是网络访问延迟可能增加。在阅读完随后的代理设置一节后,你的 DNS 请求将均通过代理发送,这将在 DNS 发送方面最大限度的保障你的隐私和安全。

vim 编辑/etc/resolv.conf,删除已有条目,并将如下内容加入其中

nameserver 8.8.8.8
nameserver 2001:4860:4860::8888
nameserver 8.8.4.4
nameserver 2001:4860:4860::8844

如果你的路由器可以自动处理 DNS,resolvconf 会在每次网络连接时用路由器的设置覆盖本机/etc/resolv.conf 中的设置,执行如下命令加入不可变标志,使其不能覆盖如上加入的配置[[3]]https://wiki.Archlinux.org/title/Domain_name_resolution#Overwriting_of_/etc/resolv.conf](https://nssurge.zendesk.com/hc/zh-cn/articles/360011927114-DNS-配置指南)。

sudo chattr +i /etc/resolv.conf

设置系统为中文

打开 System Settings > Regional Settings > Language -> Add languages 中选择中文加入,再拖拽到第一位,Apply。

再将System Settings > Regional Settings > Formats 中的值设为中文-简体中文(zh_CN)

最后重新登陆即可。

很多人会错误的更改 System Settings > Regional Settings > Formats 中的值为中文蒙古(mn_CN),默认,或者其他值,这会导致系统中一半英文一半中文。这里的值要保持默认的 en_US 或 zh_CN,或者改为你在 locale.gen 中添加的任意一种语言。

也可以用 su 切换到刚建立的用户,然后编辑 ~/.config/locale.conf 修改自己用户级别的 Locale,例如:

LANG=zh_CN.UTF-8
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=

安装 yay

AUR 为 Archlinux user repository。任何用户都可以上传自己制作的 AUR 包,这也是 Arch Linux 可用软件众多的原因。由于任何人都可上传,也存在对应的风险,一般选用大众认可的包即可。

使用 yay 可以安装 AUR 中的包。执行如下命令安装 yay。

sudo pacman -S yay

或者

pacman -S --needed git base-devel
git clone https://aur.Archlinux.org/yay-bin.git
cd yay-bin
makepkg -si

安装输入法

Fcitx5 官方文档 中文及日文输入法均体验良好。

sudo pacman -S fcitx5-im #基础包组
sudo pacman -S fcitx5-chinese-addons #官方中文输入引擎
sudo pacman -S fcitx5-anthy #日文输入引擎
yay -S fcitx5-pinyin-moegirl #萌娘百科词库
sudo pacman -S fcitx5-pinyin-zhwiki #中文维基百科词库
sudo pacman -S fcitx5-material-color #主题

更多皮肤:

设置环境变量:编辑文件 EDITOR=vim sudoedit /etc/environment 加入以下内容。konsole 以及 dolphin 都需要这些环境变量,倒是 chrome 和 firefox 都不需要就可以输入中文

GTK_IM_MODULE=fcitx
QT_IM_MODULE=fcitx
XMODIFIERS=@im=fcitx
INPUT_METHOD=fcitx
SDL_IM_MODULE=fcitx
GLFW_IM_MODULE=ibus

打开 系统设置 > 区域设置 > 输入法,先点击运行Fcitx即可,拼音为默认添加项。如你还需要更多输入法如五笔,则再点击添加输入法,找到简体中文下的五笔 ,点击添加即可加入五笔输入法。

接下来点击 拼音 右侧的配置按钮,点选云拼音在程序中显示预编辑文本 最后应用。

回到输入法设置,点击配置附加组件,找到 经典用户界面 在主题里选择一个你喜欢的颜色 最后应用。

注销,重新登陆,就可以发现已经可以在各个软件中输入中文了

配置系统默认编辑器

默认情况下,Arch Linux 在一些终端编辑场景使用 vi 编辑器,但是我们使用 vim。如果不做一个额外配置,在 git 等场景下,在终端调用编辑器会出错。编辑 EDITOR=vim sudoedit /etc/profile 文件,加入如下内容,将 vim 设置为默认 EDITOR

export EDITOR='vim'

这样就不用在每次执行命令时都指定一遍 EDITOR=vim 了。

启用蓝牙(若有)

如果你有蓝牙设备,需要安装蓝牙软件包并启用蓝牙服务。随后在系统设置中进行添加设备与连接即可。

sudo pacman -S bluez bluez-utils
sudo systemctl enable --now bluetooth

如果要连接蓝牙音频设备,需要安装 pulseaudio-bluetooth 并重启 pulseaudio

sudo pacman -S pulseaudio-bluetooth
pulseaudio -k

设置 Timeshift 快照

通过以下命令安装 Timeshiftcn / aur:

yay -S aur/timeshift

打开 Timeshift,第一次启动会自动启动设置向导。

提示:Timeshift 只支持快照操作系统安装在具有 Ubuntu 类型的子卷布局(@@home 子卷)的 BTRFS 分区。

Proxy Enviroment

set http or socks proxy environment variables

# set http proxy
export http_proxy=http://PROXYHOST:PROXYPORT

# set http proxy with user and password
export http_proxy=http://USERNAME:PASSWORD@PROXYHOST:PROXYPORT

# set http proxy with user and password (with special characters)
export http_proxy=http://`urlencode 'USERNAME'`:`urlencode 'PASSWORD'`@PROXYHOST:PROXYPORT

# set socks proxy (local DNS)
export http_proxy=socks5://PROXYHOST:PROXYPORT

# set socks proxy (remote DNS)
export http_proxy=socks5h://PROXYHOST:PROXYPORT
# export other env variables
export https_proxy=$http_proxy \
ftp_proxy=$http_proxy \
rsync_proxy=$http_proxy \
all_proxy=$http_proxy

# export other env variables (another way)
export {https,ftp,rsync,all}_proxy=$http_proxy

export HTTP_PROXY=$http_proxy \
HTTPS_PROXY=$http_proxy \
FTP_PROXY=$http_proxy \ 
RSYNC_PROXY=$http_proxy \
ALL_PROXY=$http_proxy \
NO_PROXY=$no_proxy

export {HTTP,HTTPS,FTP,RSYNC,ALL}_PROXY=$http_proxy

# set git http(s) proxy
git config --global http.sslverify false
git config --global http.proxy $http_proxy
git config --global https.proxy $http_proxy

# only for 'github.com'
git config --global http.https://github.com.proxy $http_proxy

set ssh proxy environment variables

# use 'nc' with http protocol
export ssh_proxy='ProxyCommand=nc -X connect -x PROXYHOST:PROXYPORT %h %p'

# use 'nc' with http protocol and proxy user
export ssh_proxy='ProxyCommand=nc -X connect -x PROXYHOST:PROXYPORT -P 'USERNAME' %h %p'

# use 'nc' with socks5 protocol
export ssh_proxy='ProxyCommand=nc -X 5 -x PROXYHOST:PROXYPORT %h %p'

# use 'connect' with http protocol
export ssh_proxy='ProxyCommand=connect -H PROXYHOST:PROXYPORT %h %p'

# use 'connect' with http protocol and proxy user
export ssh_proxy='ProxyCommand=connect -H USER@PROXYHOST:PROXYPORT %h %p'

# use 'connect' with HTTP_PROXY environment
export ssh_proxy='ProxyCommand=connect -h %h %p'

# use 'connect' with socks5 protocol
export ssh_proxy='ProxyCommand=connect -S PROXYHOST:PROXYPORT %h %p'

# use 'connect' with socks5 protocol and user
export ssh_proxy='ProxyCommand=connect -S USER@PROXYHOST:PROXYPORT %h %p'

# use 'connect' with SOCKS5_SERVER environment
export SOCKS5_SERVER='PROXYHOST:PROXYPORT'
export SOCKS5_USER='USERNAME'
export SOCKS5_PASSWD='PASSWORD'
export ssh_proxy='ProxyCommand=connect -s %h %p'

# connect to ssh server over proxy
ssh -o "$ssh_proxy" USER@FINAL_DEST

# set git ssh proxy
git config --global core.sshCommand "ssh -o $ssh_proxy"

set no proxy to ignore private network address

no_proxy="127.0.0.1,localhost,.localdomain.com"
no_proxy=$no_proxy,`echo 10.{0..255}.{0..255}.{0..255}|tr ' ' ','`
no_proxy=$no_proxy,`echo 172.{16..31}.{0..255}.{0..255}|tr ' ' ','`
no_proxy=$no_proxy,`echo 192.168.{0..255}.{0..255}|tr ' ' ','`
export no_proxy

# for more private network addresses, check following url
# https://segmentfault.com/q/1010000010521593
# https://en.wikipedia.org/wiki/Private_network

unset proxy environment variables

unset http_proxy https_proxy ftp_proxy rsync_proxy all_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY RSYNC_PROXY ALL_PROXY
unset {http,https,ftp,rsync,all}_proxy {HTTP,HTTPS,FTP,RSYNC,ALL}_PROXY

git config --global --unset http.proxy
git config --global --unset https.proxy
git config --global --unset core.sshCommand

git config --global --unset http.https://github.com.proxy

unset ssh_proxy

A remind:

  • /etc/environment is not a script. It only supports KEY=VALUE syntax ($variable is not supported either)
  • /etc/profile does not support export {HTTP,HTTPS,FTP,RSYNC,ALL}_PROXY=$http_proxy, it may hang your login

ubuntu gnome 下环境变量

$ sudo nano /etc/environment
http_proxy=http://127.0.0.1:7890/
HTTP_PROXY=http://127.0.0.1:7890/

https_proxy=http://127.0.0.1:7890/
HTTPS_PROXY=http://127.0.0.1:7890/

ftp_proxy=http://127.0.0.1:7890/
FTP_PROXY=http://127.0.0.1:7890/

all_proxy=socks://127.0.0.1:7891/
ALL_PROXY=socks://127.0.0.1:7891/

no_proxy=localhost,127.0.0.0/8,::1
NO_PROXY=localhost,127.0.0.0/8,::1

显卡驱动

现在是 2022 年,显卡驱动的安装在 Arch Linux 上已经变得非常容易。本文区分核芯显卡和独立显卡两大类描述显卡驱动的安装。注意,确保你已经按照本教程之前的章节安装配置好科学上网、安装好必要的包后再向下进行,不要多个教程混着看,你可能漏掉了本教程前置步骤中的某些操作,从而造成问题。

所有 AMD 显卡建议使用开源驱动。英伟达显卡建议使用闭源驱动,因为逆向工程的开源驱动性能过于低下,本文也只描述英伟达闭源驱动安装。如果你支持自由软件运动,请尽可能使用具有官方支持开源驱动的英特尔和 AMD 显卡。

核芯显卡

英特尔核芯显卡

官网文档

英特尔核芯显卡安装如下几个包即可。

sudo pacman -S mesa lib32-mesa vulkan-intel lib32-vulkan-intel

xf86-video-intel arch wiki 里写的很多发行版不建议安装它,而应使用 xorg 的 modesetting 驱动(也就是什么都不用装的意思)。经过我们测试目前确实是默认 modesetting 驱动较为稳定。

注意,只有 Intel HD 4000 及以上的核显才支持 vulkan。

AMD 核芯显卡

对于具有核芯显卡的 AMD 处理器,需要先确定核显架构(Architecture)是什么,再决定安装什么驱动。推荐在 techpowerup 网站进行查询,信息非常全面。

在确定了显卡架构后,再根据架构对照这个文档决定安装什么驱动。**对于 GCN2.0 及以下架构的老显卡,直接安装开源 ATI 驱动即可,原本闭源的老旧的 Catalyst 驱动在 2021 年已被废弃。GCN2.0 及以下架构的老显卡也不要使用开源的 AMDGPU 驱动,因为其仅处于实验性质,需要各种自定义内核编译选项与配置,非常麻烦,得不偿失。**对于新型号,即 GCN3 架构及更新型的核芯显卡,直接安装开源驱动 AMDGPU 即可,也就是以下这几个包。

sudo pacman -S mesa lib32-mesa xf86-video-amdgpu vulkan-radeon lib32-vulkan-radeon libva-mesa-driver lib32-libva-mesa-driver mesa-vdpau lib32-mesa-vdpau
  • 比如你的笔记本 cpu 是目前常见的 AMD R7 4800U,那么它的核显为 Vega 8。通过查询,可知其为 GCN 5.0 架构,那么对照 arch 官方文档,你可选择安装 AMDGPU 开源驱动。
  • 再比如你的台式机 cpu 是目前常见的 锐龙 5 3400G,那么它的核显为 Vega 11。通过查询,可知其为 GCN 5.0 架构,那么对照 arch 官方文档,你可选择安装 AMDGPU 开源驱动。
  • 再老一些的 apu A10-9700 处理器 ,它的核显为 Radeon R7。通过查询,可知其为 GCN 2.0 架构,那么对照 arch 官方文档,你选择安装 ATI 开源驱动。

独立显卡

这部分会分为仅有独立显卡(无核显)与同时拥有独立显卡和核芯显卡两种情况进行讲解。

英伟达独立显卡

较新型号的独立显卡直接安装如下几个包即可。官方文档

sudo pacman -S nvidia nvidia-settings lib32-nvidia-utils

如果是 GeForce 630 以上到 GeForce 920 以下的老卡,安装 nvidia-470xx-dkms AUR及其 32 位支持包。使用 dkms 驱动同时需要 headers。

yay -S nvidia-470xx-dkms nvidia-settings lib32-nvidia-470xx-utils linux-headers

如果是 GeForce 630 以下到 GeForce 400 系列的老卡,安装 nvidia-390xx-dkmsAUR及其 32 位支持包。使用 dkms 驱动同时需要 headers。

yay -S nvidia-390xx-dkms nvidia-settings lib32-nvidia-390xx-utils linux-headers

再老的显卡直接使用开源驱动即可。

sudo pacman -S mesa lib32-mesa xf86-video-nouveau
NVIDIA Optimus

在同时拥有核芯显卡和英伟达独立显卡的笔记本上安装驱动是大多数人关注的事情,这里着重讲述。

再次提醒请按照本书前置章节配置好系统后再进行,不要多个教程混看,尤其是一些过时的教程。尤其需要注意的是确保 base-devel 包的安装以及配置好科学上网软件,以及使用 X11 模式。

英伟达双显卡模式官方文档 /// optimus-manager 官方文档

若为同时拥有核芯显卡与英伟达独显的笔记本电脑,同样需要按照上述步骤先安装各个软件包。除此之外还需要安装 optimus-manager。可以在核芯显卡和独立显卡间轻松切换。optimus-manager 提供三种模式,分别为仅用独显,仅用核显,和 hybrid 动态切换模式。

yay -S optimus-manager optimus-manager-qt

安装完成后重启即可使用。optimus-manager 安装完成后会默认 enable optimus-manager 的服务,你可在重启前检查其状态,若没有 enable 则手动将其 enable。重启后在菜单栏搜索 optimus-manager 点击即可使用。可在其设置中设置开机自动启动。

sudo systemctl enable optimus-manager

此时你应该已经可以进行显卡切换了,如果有问题,请详细阅读 optimus-manager 的文档,里面有详细的描述。由于各类问题太多,本文不进行描述,optimus-manager 的文档很详尽,请自行查看。此处仅列出几项较为重要的注意事项:

  • 如果需要在独显和核显模式间切换,要注意你没安装各类 GPU 监控插件,它们会阻止显卡切换,导致不可预料的错误。
  • 不要使用 Nvidia Control Panel 中的Save to X Configuration file按钮。会导致配置冲突。
  • 在显卡之间的切换时,重新登陆后如在 splash screen 卡住或者黑屏,可以尝试在 tty1 tty2 之间进行切换。
  • 如果你在安装 optimus manager 并重启后,直接黑屏卡死,不能进入系统,很有可能是遇到了常见的"ACPI ISSUE",简单来说,这是笔记本制造商的实现问题。可以尝试在内核启动参数中加入acpi_osi=! acpi_osi="Windows 2009" 后再尝试。[1]

最后详细说下动态切换模式。本质上其还是使用官方的 PRIME对闭源驱动的方法进行切换。需要设置三个环境变量,或者用 nvidia-prime 包提供的命令 prime-run,二者本质也是一样的,都是设置三个环境变量。

$ sudo pacman -S nvidia-prime
# 使用prime-run前缀来用独显运行某些程序
$ prime-run some_program 

对于 AMD 核显+N 卡独显的读者,optimus-manager 对于这套组合的支持目前已经发布,最新可用版本为 1.4。


如果你不是强烈追求能效控制以及注重电池寿命的用户,那么可以不用往下看了,如果你是,那么需要针对你的硬件以及笔记本型号尝试正确的电源管理方式。此部分的设置可能导致黑屏,并且尝试过程可能较长,也会遇到各类问题,请根据你个人的操作水平自行斟酌是否操作

电源控制做的事情是,在只用核显的模式下,确保正确关闭独立显卡。而在混合模式下,绝大多数情况下 Nvidia 模块实际是始终开启的,电源控制并不生效。这件事情其实很复杂,因为对于不同的显卡型号,以及笔记本型号的组合,可行的方案都是不同的。笼统来说,最广泛适用的办法是 bbswitch。但仍不建议上来就按照此方式安装使用,因为某些特定的硬件就是会出问题,也就是黑屏。这里建议按照 optimus-manager 官方的文档一步一步来,按步骤尝试,最后找到属于你自己的电脑合适的电源管理方式。文档必须详细阅读!

针对大多数笔记本适用的 Bbswitch,此处进行安装使用的讲解。首先安装包 bbswitch。若使用其它内核,则安装包 bbswitch-dkms。

sudo pacman -S bbswitch #安装 bbswitch 切换方式

接下来右键点击 optimus-manager 的托盘设置,在 Optimus 选项卡中的 switch method 选择 Bbswitch 即可。

PRIME

To use the NVIDIA driver as an RandR 1.4 output source provider, also known as “PRIME”, the X server needs to be configured to use the NVIDIA driver for its primary screen and to use the “modesetting” driver for the other graphics device. This can be achieved by placing the following in /etc/X11/xorg.conf:

Section "ServerLayout"
    Identifier "layout"
    Screen 0 "nvidia"
    Inactive "intel"
EndSection

Section "Device"
    Identifier "nvidia"
    Driver "nvidia"
    BusID "<BusID for NVIDIA device here>"
EndSection

Section "Screen"
    Identifier "nvidia"
    Device "nvidia"
    Option "AllowEmptyInitialConfiguration"
EndSection

Section "Device"
    Identifier "intel"
    Driver "modesetting"
EndSection

Section "Screen"
    Identifier "intel"
    Device "intel"
EndSection

The X server does not automatically enable displays attached using the output sink in this configuration. To do that, use the xrandr command line tool.

For NVIDIA as an output source:

xrandr --setprovideroutputsource modesetting NVIDIA-0
xrandr --auto

AMD 独立显卡

AMD 独立显卡的驱动安装步骤实际上 AMD 核芯显卡是相同的,都需要先确定架构,然后选定正确的驱动安装即可。真正需要关注的是如何在核芯显卡和独立显卡间进行切换。可以使用 PRIME 对开源驱动的双显卡切换方式。

此外,可以使用 glmark2DRI_PRIME=1 glmark2 分别对核显和独显进行测试,选择分数更高的一个进行使用。可以在 steam 游戏的启动前缀中加入DRI_PRIME=1 mangohud %command%来使用独显。(关于 mangohud)。

笔记本上使用独立显卡运行 steam 游戏的另一个例子。

DRI_PRIME=1 steam steam://rungameid/570 #运行dota2
DRI_PRIME=1 steam steam://rungameid/730 #运行cs go

显卡性能测试

官方文档

最传统和广为人知的方式为使用glxgears命令进行测试,其属于mesa-utils包。但其仅仅只能提供简单的测试场景及帧数显示,只测试了当前 OpenGL 功能的一小部分,功能明显不足。我们推荐如下两种工具。

glmark2

glmark 提供了一系列丰富的测试,涉及图形单元性能(缓冲,建筑,照明,纹理等)的不同方面,允许进行更全面和有意义的测试。 每次测试单独计算帧速率。 最终,用户根据以前的所有测试获得了一个成绩分数。在 Archlinux 上属于包glmark2

Unigine benchmark

Unigine 3D 引擎是一个更全面的基准测试工具。 截止目前有五个版本,从旧到新分别是

  • sanctuary(2007)
  • tropics(2008)
  • heaven(2009)
  • valley(2013)
  • superposition(2017)

可从AUR下载全部版本。它们均为专有软件。

显卡信息查看

对于英伟达显卡,nvidia-settings 这个包即可全面的展示显卡相关信息。

对于 AMD 显卡,稍微麻烦一些,通过 yay 安装 radeon-profile-git 这个包,同时安装其依赖 radeon-profile-daemon,最后启动这个进程。即可以图形化的方式查看 amd 显卡信息。github 项目地址

sudo systemctl enable --now radeon-profile-daemon.service

注意,不要对左下角的 auto low high 进行更改 有 bug 会卡死。同时,显存占用在某些型号显卡上展示可能有误。

后续

如果作为一个普通使用者,到这里你的系统已经配置完毕了。不会命令行也没太大关系,你可以慢慢探索 KDE 这个桌面环境,记住时常用如下命令或 Discover 软件更新系统即可。

# 更新官方仓库
$ sudo pacman -Syyu 
# 同时更新官方仓库与AUR
$ yay -Syyu 

接下来你可以查阅娱乐、办公、多媒体等章节了解更多使用软件的安装与使用。如果你需要成为一名较为专业的人员,那么请阅读进阶、以及编程等章节。

其他

Operation too slow

修改文件/etc/pacman.conf

#XferCommand = /usr/bin/curl -C - -f %u > %o
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u

把其中一行注释取消掉(删除#)就行了。

ath9k Weak And Unstable Connection

ath9k 尝试:

$ lspci -k
...
03:00.0 Network controller: Qualcomm Atheros QCA9565 / AR9565 Wireless Network Adapter (rev 01)
 Subsystem: Lite-On Communications Inc QCA9565 / AR9565 Wireless Network Adapter
 Kernel driver in use: ath9k
 Kernel modules: ath9k
$ modprobe ath9k nohwcrypt=1 # manually
$ sudo vim /etc/modprobe.d/ath9k-nohwcrypt.conf # Using files
options ath9k nohwcrypt=1

or

yay -S backports-patches-git

ssh-agent

In order to start the agent automatically and make sure that only one ssh-agent process runs at a time, add the following to your ~/.bashrc:

$ vim ~/.bashrc
export XDG_RUNTIME_DIR=$HOME/.config
mkdir -p $XDG_RUNTIME_DIR
if ! pgrep -u "$USER" ssh-agent > /dev/null; then
    ssh-agent > "$XDG_RUNTIME_DIR/ssh-agent.env"
fi
if [[ ! "$SSH_AUTH_SOCK" ]]; then
    source "$XDG_RUNTIME_DIR/ssh-agent.env" >/dev/null
fi

Once ssh-agent is running, you will need to add your private key to its cache:

ssh-add ~/.ssh/id_rsa0

Tip: To make all ssh clients, including git store keys in the agent on first use, add the configuration setting AddKeysToAgent yes to ~/.ssh/config. Other possible values are confirm, ask and no (default). for example:

Host github-account0
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa0

Host github-account1
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_rsa1
  
AddKeysToAgent yes

Chrome 无法连接代理

通过环境变量设置代理,结果 chrome 无法浏览网页,报错 ERR_EMPTY_RESPONSE

可以 man google-chrome 找到设置代理的参数 proxy-server 指定代理,如

google-chrome-stable  --proxy-server="127.0.1:7890"

Optimization

“Arch安装器”

特点是使用 Arch Linux 主软件库,定制桌面,使用图形 LiveCD 安装等。

Archinstall

May 2nd, 2022

本文将指导使用 archinstall 安装 Arch Linux 和 KDE 桌面环境。

前言

众所周知,安装 Arch Linux 是一件非常复杂并痛苦的事情,您需要一定的 Linux 基础,然后使用命令行进行硬盘分区,安装自己需要的软件,Arch Linux 官方也并未提供 GUI 安装程序,所以很多想尝试 Arch Linux 的用户都会被劝退在安装这一步骤上。

archinstall 是一个 Python 写的 Arch Linux 安装向导程序,我们可以很方便地使用 archinstall 安装 Arch Linux。

Arch Linux 发布 2022.05.01 的 iso 后,已经默认集成了 archinstall,于是您可以参考本教程无痛安装 Arch Linux。

准备工作

首先,获取安装映像,您可以在下载页面下载最新的 iso 镜像文件,您可以选择速度最快的 mirror 进行下载,这里推荐两个下载链接

下载后您需要准备个 U 盘或移动硬盘,然后使用一些工具,比如 Rufus,这里不再阐述,其他方法请参考这里

另外,您需要确认主板 BIOS 里没有奇奇怪怪的设置,比如某些针对 Windows 系统的设置,比如快速启动、CSM 安全启动、TPM 模块等都设置需要自己调整,否则默认配置可能会导致安装完 Arch Linux 后无法进入系统引导。

安装系统

  1. 启动进入引导后,我们会看到熟悉的 Arch Linux 界面,默认进入后即可看到 Live CD 已经正常工作。

  2. 我们可以运行 installation_guide 命令查看安装文档,当然都是英文的,按 Q 退出。

  3. 我们可以直接运行 archinstall 进行图形化安装向导,然后我们会看到 archinstall 的向导界面。然后我们就一步一步来安装:

    • Archinstall language 这里可以选择 archinstall 的界面语言,很可惜,截止本文发布,并没有中文。
    • Keyboard layout 选择键盘布局,默认情况你的键盘布局应该都是 us,除非你是德国等国家的用户,那么请自行选择。
    • Mirror region 可以选择最合适的镜像,建议选择和您当前网络一致的国家或地区,记得按空格选择,然后按回车继续。
    • Drive(s) 可以选择安装的硬盘,请自行选择需要安装的硬盘,切记看清楚硬盘大小,不要装错了硬盘最后拍大腿。
    • Disk layout让您选择如何分区,如果没有特殊需求,直接选择 Wipe all selected drives and use a best-effort default partition layout,这样会把你的硬盘全部格式化,切记备份重要数据,不然安装了以后拍大腿。
      • 然后会询问您硬盘分区格式,可选 btrfsext4f2fsxfs,如果没有特殊需求,可以选最常用的 ext4
      • 然后会询问您是否要对 /home 目录单独分区,这里主要存放用户的数据,默认建议单独分区,实际操作中会分配大概 80% 的硬盘空间给 /home 目录,你也可以一股脑都分给 /,请自行决定。
    • Encryption password 选项,如果您需要对硬盘加密,可以选择,如果没需要可以跳过。
    • 然后我们直接跳过 BootloaderUse swap,因为他已经自动给您设置好了。
    • 如果您喜欢的话,可以给您的机器设置 HostnameRoot password ,如果没有特殊需求,也可以跳过,
    • 来到 User account,设置一个拥有 sudo 权限的超级用户,这个用户是日常登录和操作使用,请务牢记用户名和密码。成功后选择 Confirm and exit 即可。
    • Profile选项选择系统的使用场景,比如desktop提供kde/gnome/sway等,我们这里选择 kde
    • Audio选项选择音频服务器,有pluseaudiopipewire
    • Kernels选项可选择linux/linux-lts/linux-zen
    • 来到 Network configuration,因为我们希望安装 KDE 桌面环境,所以选择 Use NetworkManager,如果是服务器环境,可以选择 Manual configuration 手工配置网络。
    • 然后我们选择时区,进入 Timezone,按照您本地的时区来选择,可以使用 / 然后输入前几个字符快速搜索,比如 /shanghai
    • 一切准备就绪,我们可以选择 Save configuration 来保存配置,也可以直接选 Install 进行安装。系统会提示 Would you like to chroot into the newly created installation and perform post-installation configuration?,这里我们直接选择 Yes,然后进入安装。

安装完成后开启 sddm

systemctl enable sddm

然后我们使用 exit 命令退出并使用 reboot 命令重启。

pacman

pacman是arclinux中的软件管理工具,可以直接从网络上的软件仓库下载安装及删除软件,自动处理依赖关系。

详情查看 pacman(8)

安装软件

  • pacman -S 软件名: 安装软件。也可以同时安装多个包,只需以空格分隔包名即可。

    • -S, –sync

      Synchronize packages.

  • pacman -S --needed 软件名1 软件名2: 安装软件,但不重新安装已经是最新的软件。

    • –needed

      Do not reinstall the targets that are already up-to-date.

  • pacman -Sy 软件名:安装软件前,先从远程仓库下载软件包数据库(数据库即所有软件列表)。

    • -y, –refresh

      Download a fresh copy of the master package database from the server(s) defined in pacman.conf(5). Passing two –refresh or -y flags will force a refresh of all package databases, even if they appear to be up-to-date.

  • pacman -Sv 软件名:在显示一些操作信息后执行安装。

    • -v, –verbose

      Output paths such as the Root, Conf File, DB Path, Cache Dirs, etc.

  • pacman -Sw 软件名: 只下载软件包,不安装。

    • -w, –downloadonly

      Retrieve all packages from the server, but do not install/upgrade anything.

  • pacman -U 软件名.pkg.tar.gz:安装本地软件包。

    • -U, –upgrade

      Upgrade or add package(s) to the system and install the required dependencies from sync repositories.

  • pacman -U http://www.example.com/repo/example.pkg.tar.xz: 安装一个远程包(不在 pacman 配置的源里面)。

更新系统

  • pacman -Sy: 从服务器下载新的软件包数据库(实际上就是下载远程仓库最新软件列表到本地)。

  • pacman -Su: 升级所有已安装的软件包。

    • -u, –sysupgrade

      Upgrades all packages that are out-of-date.

pacman 可以用一个命令就可以升级整个系统。花费的时间取决于系统有多老。这个命令会同步非本地(local)软件仓库并升级系统的软件包:

pacman -Syu

在Arch linux中,只支持系统完整升级,不支持部分升级。

如果升级时,网络比较慢,觉得既浪费时间又浪费硬盘,实在不想升级那么多东西,可以逐个软件包升级。用下面命令可以升级核心包:

pacman -S --needed <packages...>

详解(小写为在上面固定搭配下的含义):

卸载软件

  • pacman -R 软件名: 该命令将只删除包,保留其全部已经安装的依赖关系

    • -R, –remove

      Remove package(s) from the system.

  • pacman -Rv 软件名: 删除软件,并显示详细的信息

  • pacman -Rs 软件名: 删除软件,同时删除本机上只有该软件依赖的软件。

    • -s, –recursive

      Remove each target specified including all of their dependencies, provided that (A) they are not required by other packages; and (B) they were not explicitly installed by the user.

  • pacman -Rsc 软件名: 删除软件,并删除所有依赖这个软件的程序,慎用

    • -c, –cascade

      Remove all target packages, as well as all packages that depend on one or more target packages.

  • pacman -Ru 软件名: 删除软件,同时删除不再被任何软件所需要的依赖

    • -u, –unneeded

      Removes targets that are not required by any other packages.

详解(小写为在上面固定搭配下的含义):

搜索软件

  • pacman -Ss 关键字: 在仓库中搜索含关键字的软件包(本地已安装的会标记)

    参数加q可以简洁方式显示结果,比如 pacman -Ssq gcc 会比 pacman -Ss gcc 显示的好看一些。

    • -s, –search

      This will search each package in the sync databases for names or descriptions that match regexp.

  • pacman -Sl <repo>:显示软件仓库中所有软件的列表

    • 通常这样用:pacman -Sl | 关键字

    • ``pacman -Sl | gccpacman -Ssq gcc` 很接近,但是会少一些和gcc有关但软件名不包含gcc的包。

    • -l, –list

      List all packages in the specified repositories.

  • pacman -Qs 关键字: 搜索已安装的软件包

    • -Q, –query

      Query the package database.

  • pacman -Qu: 列出所有可升级的软件包

    • -u, –upgrades

      Restrict or filter output to packages that are out-of-date on the local system.

  • pacman -Qt: 列出不被任何软件要求的软件包

    • -t, –unrequired

      Restrict or filter output to print only packages neither required nor optionally required by any currently installed package.

查询软件信息

  • pacman -Q 软件名: 查看软件包是否已安装,已安装则显示软件包名称和版本

  • pacman -Qi 软件名: 查看某个软件包信息,显示较为详细的信息,包括描述、构架、依赖、大小等等

    • -i, –info

      Display information on a given package.

  • pacman -Ql 软件名: 列出软件包内所有文件,包括软件安装的每个文件、文件夹的名称和路径

    • -l, –list

      List all files owned by a given package.

软件包组

  • pacman -Sg: 列出软件仓库上所有的软件包组

    • -g, –groups

      Display all the members for each package group specified.

  • pacman -Qg: 列出本地已经安装的软件包组和子包

  • pacman -Sg 软件包组: 查看某软件包组所包含的所有软件包

  • pacman -Qg 软件包组: 和pacman -Sg 软件包组完全一样

很多人建议通过安装软件组来安装工具链,但是这样比较浪费空间。实际上如果把gcc, qt, clang等安装上,msys2就要占掉超过10G的硬盘空间,所以个人很少直接安装软件组。

清理缓存

  • pacman -Sc:清理未安装的包文件,包文件位于 /var/cache/pacman/pkg/ 目录。

    • -c, –clean

      Remove packages that are no longer installed from the cache as well as currently unused sync databases to free up disk space. Use one –clean switch to only remove packages that are no longer installed; use two to remove all files from the cache.

  • pacman -Scc:清理所有的缓存文件。

配置 ZRAM

ZRAM 介绍

随着现代应用程序的多样化和复杂化发展,那个曾经只需要 640KB 内存就能运行市面上所有软件的时代已经一去不复返,而比应用程序发展更快的则是用户对多任务的需求。现在的主流操作系统都提供了内存压缩的功能,以保证活跃应用程序拥有尽可能多的可用内存:

  • macOS 从 OS X 10.9 之后默认开启内存压缩
  • Windows 从 Win10 TH2 之后默认开启内存压缩
  • 大部分 Android 手机厂商都默认开启了内存压缩

Android(Linux)的内存压缩是依靠 Swap 机制实现的,大部分情况下是使用 ZRAM 技术来模拟 Swap。

ZRAM 早在 2014 年就伴随 Linux 3.14 内核合入主线,但由于 Linux 用途十分广泛,这一技术并非默认启用,只有 Android 和少部分的 Linux 桌面发行版如 Fedora 默认启用了这一技术,以保证多任务场景下内存的合理分层存储。

ZRAM 运行机制

ZRAM 的原理是划分一块内存区域作为虚拟的块设备(可以理解为支持透明压缩的内存文件系统),当系统内存不足出现页面交换时,可以将原本应该交换出去的页压缩后放在内存中,由于部分被『交换出去』的页得到了压缩,因此可用的物理内存就能随之变多。

由于 ZRAM 并没有改变 Linux 内存模型的基本结构,因此我们只能利用 Linux 中 Swap 的优先级能力,将 ZRAM 作为高优先级 Swap 看待,这也解释了为什么闪存比较脆弱的手机上会出现 Swap,其本质还是 ZRAM。

随着部分手机开始使用真正的固态硬盘,也有将 Swap 放在硬盘上的手机,但是一般也都会优先使用 ZRAM。

由于这一运行机制的存在,ZRAM 可以设计得足够简单:内存交换策略交给内核、压缩算法交给压缩库,ZRAM 本身基本上只需要实现块设备驱动,因此具有极强的可定制性和灵活性,这也是 Windows、macOS 等系统所无法比拟的。

Linux5.16 中关于 ZRAM 的源码只有不到 100KB,实现非常精简,对 Linux 驱动开发感兴趣的朋友也可以从这里开始研究:linux/Kconfig at v5.16 · torvalds/linux

ZRAM 配置与自启动

确认内核是否支持&有无启用 ZRAM

既然 ZRAM 是内核模块,就需要先检查当前 Linux 机器的内核是否存在这一模块。

在配置之前,需要读者先确认一下自己的内核版本是否在 3.14 以上,部分 VPS 由于依旧使用 XenOpenVZ 等虚拟/容器化技术,内核版本往往卡在 2.6,那么这样的机器是无法开启 ZRAM 的。

uname -r

但根据内核版本判断毕竟不可靠,如 CentOS 7,虽然内核版本是 3.10,却支持 ZRAM,也有极少数发行版或嵌入式 Linux 为了降低资源占用,选择不编译 ZRAM,因此我们最好使用 modinfo 命令来检查一下有无 ZRAM 支持:

modinfo zram

部分发行版会默认启用但不配置 ZRAM,我们可以使用lsmod检查 ZRAM 是否启用:

lsmod | grep zram

启用 ZRAM 内核模块

如果确定 ZRAM 没有被启用,我们可以新建文件 /etc/modules-load.d/zram.conf,并在其中输入 zram,然后重启机器以生效。

echo 'zram' | sudo tee /etc/modules-load.d/zram.conf

上文我们提到,ZRAM 本质上是块设备驱动,那么当我们输入 lsblk 会发生什么呢?

lsblk

可以看到,其中并没有 zram 相关字眼,这是因为我们需要先新建一个块设备。

打开 ZRAM 源码 中的Kconfig文件,可以找到如下说明:

Creates virtual block devices called /dev/zramX (X = 0, 1, …). Pages written to these disks are compressed and stored in memory itself. These disks allow very fast I/O and compression provides good amounts of memory savings.

It has several use cases, for example: /tmp storage, use as swap disks and maybe many more.

See Documentation/admin-guide/blockdev/zram.rst for more information.

其中提到了一篇位于 Documentation/admin-guide/blockdev/zram.rst说明文档

根据说明文档,我们可以使用 modprobe zram num_devices=1 的方式来让内核在启用 ZRAM 模块时开启一个 ZRAM 设备(一般只需要一个就够了),但这样开启设备的方式依旧在重启后就会失效,并不方便。

好在modprobe说明文档 中提到了modprobe.d的存在:modprobe.d(5) – Linux manual page

继续阅读 modprobe.d 的文档,我们会发现它主要用于modprobe时预定义参数,即只需要输入 modprobe zram,辅以 modprobe.d中的配置,就可以自动加上参数。由于我们使用 modules-load.d 实现了 ZRAM 模块的开机自启,因此只需要在modprobe.d中配置参数即可。

按照上文所述的文档,新建文件/etc/modprobe.d/zram.conf,在其中输入options zram num_devices=1,即可配置一个 ZRAM 块设备,同样重启后生效。

echo 'options zram num_devices=1' | sudo tee /etc/modprobe.d/zram.conf

配置 zram0 设备

重启后输入lsblk,却发现所需的 ZRAM 设备依旧没有出现?

不用担心,这是因为我们还没有为这个块设备建立文件系统,lsblk虽然名字听起来像是列出块设备,但本质上读取的确是/sys目录里的文件系统信息,再将其与udev中的设备信息比对。

阅读udev的文档:udev(7) – Linux manual page,其中提到udev会从/etc/udev/rules.d目录读取设备信息,按照文档中的指示,我们新建一个名为/etc/udev/rules.d/99-zram.rules的文件,在其中写入如下内容:

echo 'KERNEL=="zram0",ATTR{disksize}="30G",TAG+="systemd"' | sudo tee /etc/udev/rules.d/99-zram.rules

其中,KERNEL属性用于指明具体设备,ATTR属性用于给设备传递参数,这里我们需要阅读zram的文档,其中提到:

Set disk size by writing the value to sysfs node ‘disksize’. The value can be either in bytes or you can use mem suffixes. Examples:

# Initialize /dev/zram0 with 50MB disksize
echo $((50*1024*1024)) > /sys/block/zram0/disksize

# Using mem suffixes
echo 256K > /sys/block/zram0/disksize
echo 512M > /sys/block/zram0/disksize
echo 1G > /sys/block/zram0/disksize

Note: There is little point creating a zram of greater than twice the size of memory since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the size of the disk when not in use so a huge zram is wasteful.

即该块设备接受名为disksize的参数,且不建议分配内存容量两倍以上的 ZRAM 空间。笔者的 Linux 环境拥有 60G 内存,考虑到实际使用情况,设置了 30G 的 ZRAM,这样一来理想情况下就能获得60G - (30G / 2) + 30G > 75G以上的内存空间,已经足够使用。读者可以根据自己的实际情况选择 ZRAM 空间大小,一般来说一开始可以设置小一些,不够用再扩大。

TAG属性用于标记设备类型(设备由谁管理),根据systemd.device的文档:systemd.device(5) – Linux manual page,大部分块设备和网络设备都建议标记 TAG 为systemd,这样systemd就可以将这个设备视作一个Unit,便于控制服务的依赖关系(如块设备加载成功后再启动服务等),这里我们也将其标记为systemd即可。

$ sudo systemctl status dev-zram0.device

可以使用如 dev-zram0.device 或者 dev-sda1.device 等方式获取设备的 Device Unit

配置结束后,再次重启 Linux,就能在lsblk 命令中看到zram0设备了。

将 zram0 设备配置为 Swap

获取到了一个 30G 大小的 ZRAM 设备,接下来需要做的就是将这个设备配置为 Swap,有经验的读者应该已经猜到接下来的操作了:

sudo mkswap /dev/zram0
sudo swapon /dev/zram0

是的,将zram0设备配置为 Swap 和将一个普通设备/分区/文件配置为 Swap 的方式是一模一样,但该如何让这一操作开机自动执行呢?

首先想到的自然是使用fstab,但好巧不巧,启用 ZRAM 内核模块使用的modules-load.d也难逃 Systemd 的魔爪:modules-load.d(5) – Linux manual page。既然从一开始就上了 Systemd 的贼船,那就贯彻到底吧!

在 Systemd 的体系下,开机自启的命令可以被注册为一个 Service Unit,我们新建一个文件/usr/lib/systemd/system/zram.service,在其中写入如下内容:

$ sudo modprobe zram
$ sudo nano /usr/lib/systemd/system/zram.service
[Unit]
Description=ZRAM
BindsTo=dev-zram0.device
After=dev-zram0.device

[Service]
Type=oneshot
RemainAfterExit=true
ExecStartPre=/sbin/mkswap /dev/zram0
ExecStart=/sbin/swapon -p 2 /dev/zram0
ExecStop=/sbin/swapoff /dev/zram0

[Install]
WantedBy=multi-user.target

接下来运行systemctl daemon-reload重载配置文件,再运行systemctl enable zram --now,如果没有出现报错,可以运行swapon -s 查看 Swap 状态,如果看到存在名为/dev/zram0的设备,恭喜你!现在 ZRAM 就已经配置完成并能实现自启动了~

sudo systemctl daemon-reload
sudo systemctl enable zram --now
swapon -s

里为了帮助读者了解 ZRAM 和 Systemd 的原理,因此采取了全手动的配置方式。如果读者觉得比较麻烦,或有大规模部署的需求,可以使用 systemd/zram-generator: Systemd unit generator for zram devices,大部分默认启用 ZRAM 的发行版(如 Fedora)都使用了这一工具,编写配置文件后运行systemctl enable /dev/zram0 --now即可启用 ZRAM。

配置双层 Swap(可选)

上一节我们配置了 ZRAM,并将其设置为了 Swap,但此时 ZRAM 依旧是不生效的。为什么呢?眼尖的读者应该发现了,/swapfile的优先级高于/dev/zram0,这导致当 Linux 需要交换内存时,依旧会优先将页换入/swapfile,而非 ZRAM。

解决这个问题可以通过两种方式:禁用 Swapfile,或者降低 Swapfile 的优先级,这里为了避免 ZRAM 耗尽后出现 OOM 导致服务掉线,我们采取后者,即配置双层 Swap,当高优先级的 ZRAM 耗尽后,会继续使用低优先级的 Swapfile。

我们打开 Swapfile 的配置文件(笔者的配置文件在/etc/fstab中),增加 pri(Priority) 参数:

$ sudo nano /etc/fstab
/swapfile none swap sw,pri=1 0 0

如果使用其他方式配置 Swapfile(如 Systemd),只要保证执行swapon时携带-p参数即可,数字越低,优先级越低。对于 ZRAM 同理,如上文的zram.service中就配置 ZRAM 的优先级为 2。

设置后重启 Linux,再次执行 swapon -s 查看 Swap 状态,保证 ZRAM 优先级高于其他 Swap 优先级即可:

swapon -s

ZRAM 监控

启用 ZRAM 后,我们该如何查看 ZRAM 的实际效用,如压缩前后大小,以及压缩率等状态呢?

最直接的办法自然是查看驱动的 源码,和 文档,可以发现函数mm_stat_show()定义了/sys/block/zram0/mm_stat文件的输出结果,从左到右分别代表:

$ cat /sys/block/zram0/mm_stat
# orig_data_size - 当前压缩前大小 (Byte)
4096

# compr_data_size - 当前压缩后大小 (Byte)
74

# mem_used_total - 当前总内存消耗,包含元数据等 Overhead(Byte)
12288

# mem_limit - 当前最大内存消耗限制(页)
0

# mem_used_max - 历史最高内存用量(页)
1223118848

# same_pages - 当前相同(可被压缩)的页
0

# pages_compacted - 历史从 RAM 压缩到 ZRAM 的页
50863

# huge_pages - 当前无法被压缩的页(巨页)
0

该文件适合输出到各种监控软件进行监控,但无论是 Byte 还是页,这些裸数值依旧不便阅读,好在util-linux包提供了一个名为zramctl的工具(和 systemctl 其实是雷锋与雷峰塔的关系),在安装util-linux后执行zramctl,即可获得结果:

zramctl

根据上文mm_stat的输出,可以类推每项数值的含义,或者我们可以找到zramctl源码,了解每项输出的含义与单位:

static const struct colinfo infos[] = {
    [COL_NAME]      = { "NAME",      0.25, 0, N_("zram device name") },
    [COL_DISKSIZE]  = { "DISKSIZE",     5, SCOLS_FL_RIGHT, N_("limit on the uncompressed amount of data") },
    [COL_ORIG_SIZE] = { "DATA",         5, SCOLS_FL_RIGHT, N_("uncompressed size of stored data") },
    [COL_COMP_SIZE] = { "COMPR",        5, SCOLS_FL_RIGHT, N_("compressed size of stored data") },
    [COL_ALGORITHM] = { "ALGORITHM",    3, 0, N_("the selected compression algorithm") },
    [COL_STREAMS]   = { "STREAMS",      3, SCOLS_FL_RIGHT, N_("number of concurrent compress operations") },
    [COL_ZEROPAGES] = { "ZERO-PAGES",   3, SCOLS_FL_RIGHT, N_("empty pages with no allocated memory") },
    [COL_MEMTOTAL]  = { "TOTAL",        5, SCOLS_FL_RIGHT, N_("all memory including allocator fragmentation and metadata overhead") },
    [COL_MEMLIMIT]  = { "MEM-LIMIT",    5, SCOLS_FL_RIGHT, N_("memory limit used to store compressed data") },
    [COL_MEMUSED]   = { "MEM-USED",     5, SCOLS_FL_RIGHT, N_("memory zram have been consumed to store compressed data") },
    [COL_MIGRATED]  = { "MIGRATED",     5, SCOLS_FL_RIGHT, N_("number of objects migrated by compaction") },
    [COL_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
};

部分未默认输出的数值可以通过zramctl --output-all输出:

zramctl --output-all

这个工具的输出结果混淆了 Byte 和页,混淆了历史最高、累计和当前的数值,且将不设置的参数(如内存限制)显示为 0B,因此输出结果仅作为参考,可读性依旧不高,一般来说只用了解DATACOMPR字段即可。

结合zramctlmm_stat的输出,不难发现我们配置的 ZRAM 大小其实是未压缩的大小,而非是压缩后的大小,前面我们提到了一个算法,当 ZRAM 大小为 30GB 且压缩率为 2:1 时,可以获得60G - (30G / 2) + 30G > 75G的可用内存,这就是假设了 30GB 的未压缩数据可以压缩到 15G,占用 15G 物理内存空间,即60G - (30G / 2),然后再加上 ZRAM 能存储最大的内存数据 30G 计算出来。

计算压缩率的方式为 DATA / COMPR。,以 ElasticSearch 的工作负载为例,默认压缩率为1.1G / 97M = 11.6(如果是4K 74B则是没有负载)。

ZRAM 调优

尽管 ZRAM 的设置非常简单,其依然提供了大量可配置项供用户调整,如果在默认配置 ZRAM 后依旧觉得不满意,或者想要进一步发掘 ZRAM 的潜力,就需要对其进行优化。

选择最适合的压缩算法

ZRAM 目前的默认压缩算法一般是lzo-rle,但其实 ZRAM 支持的压缩算法有很多,我们可以通过 cat /sys/block/zram0/comp_algorithm 获取支持的算法,当前启用的算法被[]括起来:

cat /sys/block/zram0/comp_algorithm

压缩是一个时间换空间的操作,也就意味着这些压缩算法并不存在绝对优劣,只存在不同情况下的取舍,有的压缩率高、有的带宽大、有的 CPU 消耗少……在不同的硬件上,不同的选择也会遇到不同的瓶颈,因此只有进行真实的测试,才能帮助选择最适合的压缩算法。

根据工作负载和需求的不同,读者可以选择适合自己的参数,也可以结合上面提到的多级 Swap,将 ZRAM 进一步分层,使用最高效的内存作为高优先级 Swap,压缩率最高的内存作为中低优先级 Swap。

如果测试机和生产环境的架构/硬件存在差异,可以将测试过程中导出的内存拷贝到生产环境,前提是两者运行相同的工作负载,否则测试内存无参考价值。

配置 ZRAM 调优参数

配置压缩算法,获得最佳压缩率

首先将压缩算法从默认的lzo-rle切换为lz4hc,根据 ZRAM 的文档,只需要将压缩算法写入/sys/block/zram0/comp_algorithm即可,考虑到我们配置/sys/block/zram0/disksize时的操作,我们重新编辑/etc/udev/rules.d/99-zram.rules文件,将其内容修改为:

$ sudo nano /etc/udev/rules.d/99-zram.rules
KERNEL=="zram0",ATTR{comp_algorithm}="lz4hc",ATTR{disksize}="30G",TAG+="systemd"

需要注意的是,必须先指定压缩算法,再指定磁盘大小,无论是在配置文件中还是直接echo参数到/sys/block/zram0设备上,都需要按照文档的顺序进行操作。

重启机器,再次执行cat /sys/block/zram0/comp_algorithm,就会发现当前压缩算法变成了lz4hc

配置 page-cluster,避免内存带宽和 CPU 资源的浪费

简单来说,page-cluster的作用就是每次从交换设备读取数据时多读 2^n 页,一些块设备或文件系统有簇的概念,读取数据也是按簇读取,假设内存页大小为 4KiB,而每次读取的一簇数据为 32KiB,那么把多读出来的数据也换回内存,就能避免浪费,减少频繁读取磁盘的次数,这一点在 Linux 的文档中也有提到:linux/vm.rst · torvalds/linux。默认的page-cluster大小为 3,即每次会从磁盘读取4K*2^3=32K的数据。

了解了page-cluster的原理后,我们会发现 ZRAM 并不属于传统的块设备,内存控制器默认设计就是按页读取,因此这一适用于磁盘设备的优化,在 ZRAM 场景下却是负优化,反而会导致过早触及内存带宽瓶颈和 CPU 解压缩瓶颈而导致性能下降,这也就可以解释为什么上面的表格中随着page-cluster的提升,吞吐量同样提升,而 IOPS 却变得更小,如果内存带宽和 CPU 解压缩不存在瓶颈,那么 IOPS 理论上应该保持不变。

考虑到无论是理论上,还是实际测试,page-cluster都是一个多余的优化,我们可以直接将其设置为 0。直接编辑/etc/sysctl.d/99-sysctl.conf,在结尾新增一行:

$ sudo nano /etc/sysctl.d/99-sysctl.conf
vm.page-cluster=0

运行sysctl -p,即可让该设置生效,无需重启。

OOM Killer

SysRq

SysRqarch wiki)键在 QWERT 键盘上与 PrtSc 同键,通过按下 ALT+SysRq+<command key> 可以直接向linux kernel发送预设的系统操作指令。 这套组合键提供了一系列在系统崩溃时常用到的功能,比如同步数据、杀进程、卸载文件系统,甚至系统重启.

启用SysRq

对内核的要求

启用 SysRq 的前提是在linux kernel编译时启用了 CONFIG_MAGIC_SYSRQ 选项.

在目前主流的发行版linux中都启用了该选项,但若你是自己编译的内核,则有必要搜索一下内核的config文件了,确保里面有一句

CONFIG_MAGIC_SYSRQ=y

内核中还有一个与SysRq相关的配置项:

CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x01b6

这个配置项指定了默认SysRq的值,这个值表示kernel会对哪些功能产生反应。

查看当前SysRq的值

我们可以通过查看 /proc/sys/kernel/sysrq 的值来判断Kernel会对哪些功能产生反应.

$ cat /proc/sys/kernel/sysrq
176 # Ubuntu 默认值

这里你会看到一个数字,这个数字可以转换成一个9位比特的形式,其中每一位的比特都有一个含义如下:

数字 位数 意义
0 1 完全禁用sysrq
1 1 允许所有的sysrq功能
2 2 允许控制终端日志级别
4 3 允许控制键盘输入类型(SAK,unraw)
8 4 允许调试进程dump
16 5 允许执行sync命令
32 6 允许重新挂载文件系统为之读
64 7 允许发送信号给进程(term,kill,oom-kill)
128 8 允许重启/关机
256 9 允许调整实时任务的优先级

因此,这里的 16 表示允许通过 SysRq 来同步数据到磁盘中去, 而数字 130 转换成二进制就是 010000010,根据表中的对应关系很容易看出允许重启/关机以及调整终端日志级别。

更改SysRq的值

如果只是希望临时更改 SysRq 的值,那么很简单,只需要将新的值写入到 /proc/sys/kernel/sysrq 中去

echo "1" |sudo tee /proc/sys/kernel/sysrq

或者通过 sysctl 来进行设置

sysctl -w kernel.sysrq=1

如果需要每次启动时都自动修改SysRq的值,则需要修改配置文件

echo "kernel.sysrq = 1" | sudo tee -a /etc/sysctl.d/99-sysctl.conf

使用SysRq

使用SysRq有两种方式:

一种是直接通过键盘 Alt+SysRq+<command key>(部分笔记本上是Alt+Fn+PrtSrc+<command key>)来出发,

还有一种是直接通过 /proc/sysrq-trigger 接口来完成.

echo “ b ” | sudo tee /proc/sysrq-trigger

其中,这里每个 command-key 都对应一种kernel的行为,而且需要说明的是,不同种类的键盘上,相同kernel行为对应的 command-key 居然是不同的!

下面表格就是各个kernel行为对应的 command-key 的说明:

Action QWERTY Dvorak AZERTY Colemak
设置控制台日志级别(console_loglevel),它决定了哪些kernel信息会被输出到控制台上 0 - 9 0 - 9 0 - 9(without ⇧ Shift) 0 - 9
不同步并卸载文件系统,立即重启系统 b x b b
让系统立即崩溃. 在配置得当的情况下会产生一个 crashdump c j c c
显示所有排它锁 (需要内核启用CONFIG_LOCKDEP选项) d e d s
发送 SIGTERM 信号到除了 init (PID 1) 外的所有进程 e . e f
触发 oom_kill, 会随机杀掉一个进程以缓解 OOM f u f t
当进入内核模式时,切换到内核的 framebuffer 控制台. 若有内核调试器 kdb,则进入该调试器中 g i g d
在控制台上输出一个简短的帮助信息. (其他不能识别的key也会输出帮助信息) h d h h
发送 SIGKILL 信号到除了 init (PID 1) 外的所有进程 i c i u
强制通过 FIFREEZE ioctl 冻结文件系统. j h j n
杀掉当前虚拟控制台中的所有进程 (包括 X 和 SVGALib 程序). k t k e
列出所有活动CPU上的 stack backtrace l n l i
在控制台上输出当前内存信息 m m , m
重置所有高优先级和实时任务的 nice 级别 n b n k
关闭系统 o r o y
在控制台输出当前寄存器和标志位信息 p l p ;
Display all active high-resolution timers and clock sources. q ' a q
将键盘从 raw 模式(常被诸如X11和SVGALib这样的程序所使用)切换到 XLATE模式 r p r p
同步所有已挂载的文件系统 s o s r
在控制台输出当前任务列表 t y t g
重新以只读模式重新挂载所有已挂载的文件系统 u g u l
强制恢复 framebuffer console. 若为ARM处理器,则会导致 ETM buffer dump. v k v v
显示所有阻塞状态(状态为D)的任务 w , z w
Used by xmon interface on PPC/PowerPC platforms. x q x x
显示全局的CPU寄存器内容 (仅对SPARC-64平台有效) y f y j
Dump the ftrace buffer z ; w z
输出一份简单的系统支持SysRq的键列表 space space space space
常见的几种功能键组合

下面列出几个常见的功能键组合:

R-E-I-S-U-B:安全重启系统

这套组合键大致相当于reboot命令:

  • unRaw – 把键盘设置为 XLATE 模式,使按键可以穿透 x server 捕捉传递给内核
  • tErminate – 向除 init 外进程发送 SIGTERM 信号,让其自行结束. 这一步推荐等待30秒让进程有足够的时间进行收尾的嗯做。
  • kIll - 向除 init 以外所有进程发送 SIGKILL 信号,强制结束进程. 这一步推荐等待10秒,保证所有进程都退出了
  • Sync – 同步缓冲区数据到硬盘,避免数据丢失. 这一步在能看到输出的情况下等到"Emergency Sync complete" 后再做后续动作,否则推荐等待10秒
  • Unmount – 将所有已经挂载的文件系统 重新挂载为只读. 该操作通常也有一定延时,请等到"Emergency Remount complete" 出现过后再进行后续操作,否则推荐等待10秒
  • reBoot - 立即重启计算机

恢复系统挂起

若仅仅是因为资源消耗过量引起系统挂起就重启系统显然是不好的,我们可以尝试通过回收一些资源的方式来回复系统挂起。

SysRq中用来结束进程的command-key包括 E-I-K-F,其中:

  • E 和 I 太凶残,它会杀掉除了 init 外的所有进程,属于杀敌一千自损八百的操作。因此在一般情况下不会轻易使用
  • F 则是利用 OOM-Kiler选择一个进程来结束,对于由于内存不足引起的挂起比较有效,但有时候OOMKiller也可能会误判杀掉一些长期运行的后台程序。
  • K 杀掉与当前控制台有关的进程组,比较推荐用这种方法回复系统

此外,若系统挂起是由于实时任务消耗太多CPU引起的,则可以通过 N 来降低实时任务运行的优先级来缓解挂起症状。

获取系统信息

SysRq还提供了几个用于获取系统信息的commandkey,在恢复系统挂起前推荐执行这些commandkey,以记录下当前系统状态。

  • M

    打印内存使用信息

  • W

    打印CPU寄存器上下文和程序调用栈回溯信息

  • P

    打印CPU寄存器信息,比如正在执行的进程名,运行函数,寄存器上下文,以及程序的调用栈回溯等

  • T

    打印进程列表,各进程的名称,进程 PID,父 PID 兄弟 PID 以及进程运行状态等相关信息

查看SysRq的输出信息

从上面的列表中我们可以看到,使用SysRq能够输出大量的信息。这些信息,默认会输出到syslog中. 同时,若设置的 console_loglevel(0-9) 大于 default_message_loglevel 则输出也会输出到本地控制台终端上去。 另外,若设置的 console_loglevel 大于 default_message_loglvel 则输出还会通过netconsole输出到远程机器上去。

总体来说,syslog中记录的日志应该是最完整的,然而由于负责记录日志的 syslogd 本身是一个用户进程,在某些情况下可能会被杀掉,从而导致日志记录不下来。

$ echo " " | sudo tee /proc/sysrq-trigger
$ sudo dmesg | tail -n 1
[17899.255261] sysrq: SysRq : HELP : loglevel(0-9) reboot(b) crash(c) terminate-all-tasks(e) memory-full-oom-kill(f) kill-all-tasks(i) thaw-filesystems(j) sak(k) show-backtrace-all-active-cpus(l) show-memory-usage(m) nice-all-RT-tasks(n) poweroff(o) show-registers(p) show-all-timers(q) unraw(r) sync(s) show-task-states(t) unmount(u) force-fb(V) show-blocked-tasks(w) dump-ftrace-buffer(z) 

systemd-oomd.service

简介

systemd-oomd是为了改善Linux的内存不足/内存压力行为而开发的,基于Facebook的内存不足守护程序代码,已经扩展到不仅适用于Linux服务器,也适用于桌面系统。systemd-OOMD可以监测资源争用情况,当内存/SWAP压力超过预定义的阈值时,可以杀死选定的进程。

systemd-oomd守护进程会对启用了OOMD的cgroups进行监视,并根据内存压力或交换使用情况进行消杀。systemd-oomd行为可以通过新的oomd.conf配置文件进行配置。这个守护进程只有在设置了EnableOomdKill的情况下才会杀死组,因为显然不想因为内存使用情况而随机杀掉进程。

EndeavourOS

Usage

sudo pacman -S fcitx5-im fcitx5-chinese-addons fcitx5-pinyin-zhwiki android-tools aria2 goldendict translate-shell kvantum google-chrome okular git xmind ttf-sarasa-gothic variety audacious

fcitx5 详细看前面。

KDE Connect

sudo pacman -S kdeconnect sshfs

Kdeconnectd

$ cat /etc/xdg/autostart/org.kde.kdeconnect.daemon.desktop
[Desktop Entry]
Type=Application
Exec=/usr/lib/x86_64-linux-gnu/libexec/kdeconnectd
X-KDE-StartupNotify=false
X-KDE-autostart-phase=1
X-KDE-Wayland-Interfaces=org_kde_kwin_fake_input
X-GNOME-Autostart-enabled=true
NoDisplay=true
Icon=kdeconnect

Name=KDE Connect

只有 KDE Connect 在连接的状态下,indicator 才会显示。

ZFS

sudo pacman -S zfs-dkms
sudo modprobe zfs
echo zfs | sudo tee /etc/modules-load.d/zfs.conf

Qemu

sudo pacman -S qemu-base samba tigervnc

plocate

依云推荐,a much faster locate

Zstd

Redshift

安装

yay -S redshift-minimal

GeoNames.org 找到经纬度并测试:

redshift -l 30.58333:114.26667 # WuHan

参考 How to get the display number I was assigned by X 获得 DISPLAY Number:

echo $DISPLAY # OR
cat /proc/$$/environ | tr '\0' '\n' | grep '^DISPLAY='

参考 Redshift fails to run as systemd unit, works in terminal 建立 ~/.config/systemd/user/redshift.service 文件(官方给的模板不行):

[Unit]
Description=Redshift display colour temperature adjustment
Documentation=http://jonls.dk/redshift/
After=display-manager.service

[Service]
Environment=DISPLAY=:0
ExecStart=/usr/bin/redshift -l 30.58333:114.26667
Restart=always
RestartSec=20

[Install]
WantedBy=default.target

服务操作

systemctl --user start redshift.service
systemctl --user enable redshift.service
journalctl --user -u redshift.service -f

字体调校

简介

很长时间以来,Linux上的中文字体呈现一直……不容乐观。但是随着 FreeType2 由于专利过期默认开启了高质量的 LCD 优化,以及一批高质量的开源字体的公布,Linux 上的中文字体渲染已经可以和 macOS 扳扳手腕了。

当然,这里是 Linux,你需要一点小小的配置。

现在,什么也不用做,打开一个中文页面,应该已经不会存在豆腐块了。Noto 字体家族正是得名于此。 Noto -> No Toufu -> 没有豆腐块。然而……看上去不大对劲,不是么?…怎么默认到日语字形上去了。这就是为什么我们需要 Fontconfig

Fontconfig 是一个用来 配置 字体渲染的程序。也就是说,fontconfig 本身没有将字体渲染成位图的能力,真正执行这个工作的是 FreeType,但是它能向程序提供可用字体列表并指导 FreeType 引擎将字体正确地渲染出来。

常用命令
FC_DEBUG=4 google-chrome-stable # 看 FcConfigSubstitute donePattern 部分,这就是实际作用的字体
fc-match --sort sans # 看请求 sans 字体实际返回的是什么字体 
pacman -Qqo /usr/share/fonts # 查看安装了哪些字体
fc-list | grep Noto # 列出所有 Noto 字体
选字体

西文要求可以清晰区分"1lI"和"O0o",当然等宽字体是必须可以区分了:

  • 西文无衬线字体
    • Noto Sans:noto-fonts
    • Ubuntu
  • 西文有衬线字体
    • Noto Serif:noto-fonts
    • Tinos:nerd-fonts-tinos
  • 西文等宽字体:
    • Noto Sans Mono:noto-fonts
    • Monaco: ttf-monaco
    • Hack:ttf-hack
    • Ubuntu Moon
  • 中文无衬线字体
    • Noto Sans CJK SC: noto-fonts-cjk
    • WenQuanYi Zen Hei:wqy-zenhei
    • WenQuanYi Micro Hei: wqy-microhei
    • HarmonyOS Sans SC:harmonyos-sans-git
  • 中文有衬线字体
    • Noto Serif CJK SC
    • Adobe Song Std:ttf-adobe-song 中文宋体(有衬线)
    • Adobe Fangsong Std:ttf-adobe-fangsong 中文仿宋(有衬线)
    • Adobe Kaiti Std:ttf-adobe-kaiti 中文楷体(书法)
  • 中文等宽字体
    • Noto Sans Mono CJK SC
    • WenQuanYi Zen Hei Mono
  • 符号表情
    • Noto Color Emoji: noto-fonts-emoji
    • Twemoji: ttf-twemoji
  • CJK 异形字
    • NotoSansCJK-Regular: noto-fonts-cjk
    • NotoSerifCJK-Regular

Noto系列字体非常全,可用于追求一致的字体体验。还有 Adobe Source Hans 字体也很全,更多字体查看 Fonts

配置

以 《Linux字体美化实战(Fontconfig配置)》及fonts.conf 为蓝本,同时参考了如下部分配置文件:

  1. 双猫CC 字体顺序与异形字设置:《用 fontconfig 治理 Linux 中的字体dotfiles 测试异形字(在firefox上)
  2. 喵’s StackHarbor的 Noto Color Emoji 设置:《Fontconfig 和 Noto Color Emoji 和抗锯齿local.conf
  3. Github 仓库《Linux字体美化实战》例子改版:best-fonts

基本思路:只有一个,设置默认字体为通用字体族名。设置完这个记得把所有软件的自定义字体设置里面无衬线设置为 sans-serif,有衬线设置为 serif,等宽设置为 monospace,这样才会遵守这里的回退顺序。

建立 ~/.config/fontconfig/fonts.conf 并添加如下内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>

    <!-- ========================== 第三部分 扫描阶段 =================================== -->

    <!-- 规范化已安装核心字体的属性(西文字族名改为类名,中文字族名改为"zhXXX",其他为"zzXXX") -->
    <!-- English -->
    <!-- En Sans -->
    <match target="scan">
        <test name="family">
            <string>Noto Sans</string>
        </test>
        <edit name="family">
            <string>Sans</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>en</string>
            </langset>
        </edit>
    </match>
    <!-- En Serif -->
    <match target="scan">
        <test name="family">
            <string>Noto Serif</string>
        </test>
        <edit name="family">
            <string>Serif</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>en</string>
            </langset>
        </edit>
    </match>
    <!-- En Mono -->
    <match target="scan">
        <test name="family">
            <string>Noto Sans Mono</string>
        </test>
        <edit name="family">
            <string>Monospace</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>en</string>
            </langset>
        </edit>
    </match>
    <!-- Simplified Chinese -->
    <!-- ZH Sans -->
    <match target="scan">
        <test name="family">
            <string>Noto Sans CJK SC</string>
        </test>
        <edit name="family">
            <string>zhSans</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>zh-cn</string>
            </langset>
        </edit>
    </match>
    <!-- ZH Serif -->
    <match target="scan">
        <test name="family">
            <string>Noto Serif CJK SC</string>
        </test>
        <edit name="family">
            <string>zhSerif</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>zh-cn</string>
            </langset>
        </edit>
    </match>
    <!-- ZH Monospace -->
    <match target="scan">
        <test name="family">
            <string>Noto Sans Mono CJK SC</string>
        </test>
        <edit name="family">
            <string>zhMonospace</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>zh-cn</string>
            </langset>
        </edit>
    </match>
    <!-- Emoji -->
    <match target="scan">
        <test name="family">
            <string>Noto Color Emoji</string>
        </test>
        <edit name="family">
            <string>zzFailback</string>
            <string>zzSymbol</string>
        </edit>
        <edit name="familylang">
            <string>en</string>
            <string>en</string>
        </edit>
        <edit name="style">
            <string>Regular</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>none</string>
            </langset>
        </edit>
    </match>

    <!-- CJK(Chinese Japanese Korean),作为中文下的备用字体 -->
    <!-- postscriptname 就是字体文件名,这样可以包括所有 SC/TC/HK/JP/KR 字体 -->
    <!-- Sans CJK -->
    <match target="scan">
        <test name="postscriptname">
            <string>NotoSansCJK-Regular</string>
        </test>
        <edit name="family">
            <string>zzFailback</string>
            <string>zzSnasCJK</string>
        </edit>
        <edit name="familylang">
            <string>en</string>
            <string>en</string>
        </edit>
        <edit name="style">
            <string>Regular</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>zh-cn</string>
            </langset>
        </edit>
    </match>
    <!-- Serif CJK -->
    <match target="scan">
        <!-- postscriptname 就是字体文件名 -->
        <test name="postscriptname">
            <string>NotoSerifCJK-Regular</string>
        </test>
        <edit name="family">
            <string>zzFailback</string>
            <string>zzSerifCJK</string>
        </edit>
        <edit name="familylang">
            <string>en</string>
            <string>en</string>
        </edit>
        <edit name="style">
            <string>Regular</string>
        </edit>
        <edit name="lang">
            <langset>
                <string>zh-cn</string>
            </langset>
        </edit>
    </match>

    <!-- ========================== 第四部分 匹配阶段 =================================== -->

    <!-- 第一步,替换所有未安装的常见字体:西文替换为对应的字体类,中文替换为"西文字体类+zhXXX",保持原有绑定不变 -->
    <alias binding="same">
        <family>mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Consolas</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Courier</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Courier New</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Fixedsys</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Console</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Terminal</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Sans Typewriter</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Andale Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Menlo</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Monaco</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Andale Mono WT</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Biwidth</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bitstream Vera Sans Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Cousine</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>DejaVu Sans Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Droid Sans Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Fira Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Fixed</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>FreeMono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Inconsolata</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Liberation Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Luxi Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Nimbus Mono L</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Code Pro</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Terminus</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Ubuntu Mono</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Unibit</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Unifont</family>
        <prefer>
            <family>Monospace</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>sans serif</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>sans-serif</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Arial</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Arial Black</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Calibri</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Candara</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Century Gothic</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Copperplate Gothic</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Corbel</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Franklin Gothic Medium</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Impact</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Sans Unicode</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Microsoft Sans Serif</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>MS Sans Serif</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>News Gothic MT</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Segoe</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Segoe UI</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Tahoma</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Trebuchet MS</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Verdana</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Arial Narrow</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Arial Unicode MS</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Franklin Gothic Bold</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Franklin Gothic Heavy</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Gill Sans MT</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Haettenschweiler</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Tw Cen MT</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Avenir</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Avenir Next</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Avenir Next Condensed</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Charcoal</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Chicago</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Futura</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Geneva</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Gill Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Helvetica</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Helvetica Neue</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Grande</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Optima</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Thonburi</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Andika</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Akzidenz-Grotesk</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Arev Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Arimo</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bitstream Vera Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Cantarell</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Carlito</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>DejaVu Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>DejaVu Sans Condensed</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Droid Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Droid Sans Fallback</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Fira Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>FreeSans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Frutiger</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Frutiger Linotype</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Gadget</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Gotham</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Liberation Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Liberation Sans Narrow</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Linux Biolinum</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Luxi Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Myriad</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Myriad Pro</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Nimbus Sans L</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans UI</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Open Sans</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Roboto</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Roboto Condensed</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Sans Pro</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Ubuntu</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Ubuntu Condensed</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Univers</family>
        <prefer>
            <family>Sans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Book Antiqua</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Calisto MT</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Cambria</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Constantia</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Gabriola</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Georgia</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>MS Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Palatino Linotype</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka Banner</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka Display</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka Heading</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka Small</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka Subheading</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Sitka Text</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Times New Roman</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Baskerville Old Face</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bell MT</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bodoni MT</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bookman Old Style</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Californian FB</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Centaur</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Century</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Century Schoolbook</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Cooper Black</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Elephant</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Garamond</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Goudy Old Style</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>High Tower Text</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Bright</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lucida Fax</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Perpetua</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Rockwell</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Baskerville</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Big Caslon</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Didot</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Hoefler Text</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>New York</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Palatino</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Times</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bitstream Charter</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bitstream CyberBase</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bitstream Cyberbit</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Bitstream Vera Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Century Schoolbook L</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Charis SIL</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Code2000</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Code2001</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>DejaVu Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>DejaVu Serif Condensed</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Droid Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>FreeSerif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Gentium</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Junicode</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Liberation Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Linux Libertine</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Luxi Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>New Athena Unicode</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Nimbus Roman No9 L</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Serif</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Old Standard TT</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Serif Pro</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Tinos</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Utopia</family>
        <prefer>
            <family>Serif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Han Sans</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Han Sans CN</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Han Sans SC</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Han Sans TC</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Source Han Sans TWHK</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>思源黑体</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>思源黑體</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>思源黑体 CN</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>思源黑體 TWHK</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans CJK</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans CJK Simplified Chinese</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans CJK Traditional Chinese</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans S Chinese</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Noto Sans T Chinese</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Hiragino Sans GB</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>冬青黑體簡體中文</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>冬青黑体简体中文</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe Heiti Std</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe 黑体 Std</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe Fan Heiti Std</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe 繁黑體 Std</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Hei</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>LiHei Pro</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>STHeiti</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>SimHei</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>黑体</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Heiti TC</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>黑體-繁</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>黑体-繁</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Heiti SC</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>黑體-簡</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>黑体-简</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>STXihei</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>华文细黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lantinghei SC</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>蘭亭黑-簡</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>兰亭黑-简</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Lantinghei TC</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>蘭亭黑-繁</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>兰亭黑-繁</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Microsoft JhengHei</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Microsoft JhengHei UI</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>微軟正黑體</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Microsoft YaHei</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Microsoft YaHei UI</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>微软雅黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>WenQuanYi Bitmap Song</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>WenQuanYi Zen Hei</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驛微米黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驿微米黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>WenQuanYi Zen Hei Mono</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驛等寬微米黑</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驿等宽微米黑</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>WenQuanYi Zen Hei</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驛正黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驿正黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>WenQuanYi Zen Hei Mono</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驛等寬正黑</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驿等宽正黑</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>WenQuanYi Zen Hei Sharp</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驛點陣正黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文泉驿点阵正黑</family>
        <prefer>
            <family>Sans</family>
            <family>zhSans</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe Song Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe 宋体 Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe Ming Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe 明體 Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UMing CN</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UMing HK</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UMing TW</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UMing TW MBE</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Apple LiSung</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>GB18030 Bitmap</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>LiSong Pro</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Ming(for ISO10646)</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>STSong</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>华文宋体</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>STZhongsong</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>华文中宋</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>SimSun</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>宋体</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>NSimSun</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>新宋体</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>SimSun-ExtB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>SimSun-18030</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>宋体-18030</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>NSimSun-18030</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>新宋体-18030</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Songti SC</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>宋體-簡</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>宋体-简</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Songti TC</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>宋體-繁</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>宋体-繁</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PLBaosong2GBK Light</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL报宋二GBK</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL SungtiL GB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL简报宋</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PLMingU20 Light</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL明體U20-L</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL Mingti2L Big5</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL細上海宋</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL ShanHeiSun Uni</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL细上海宋Uni</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL細上海宋Uni</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL New Sung</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL新宋</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL New Sung Mono</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL新宋 Mono</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>MingLiU</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>細明體</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>MingLiU-ExtB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>細明體-ExtB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>PMingLiU</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>新細明體</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>PMingLiU-ExtB</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>新細明體-ExtB</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>MingLiU_HKSCS</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>細明體_HKSCS</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>MingLiU_HKSCS-ExtB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>細明體_HKSCS-ExtB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe Fangsong Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe 仿宋 Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>FangSong_GB2312</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>仿宋_GB2312</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>FangSong</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>仿宋</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>STFangsong</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>华文仿宋</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe Kaiti Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Adobe 楷体 Std</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UKai CN</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UKai HK</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UKai TW</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL UKai TW MBE</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>BiauKai</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Kai</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>DFKai-SB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>標楷體</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>STKaiti</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>华文楷体</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>KaiTi</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>楷体</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Kaiti SC</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>楷體-簡</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>楷体-简</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>Kaiti TC</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>楷體-繁</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>楷体-繁</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>SimKai</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>KaiTi_GB2312</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>楷体_GB2312</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL KaitiM GB</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL简中楷</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL New Kai</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL新中楷</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL KaitiM Big5</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL中楷</family>
        <prefer>
            <family>Monospace</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>AR PL ZenKai Uni</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>
    <alias binding="same">
        <family>文鼎PL中楷Uni</family>
        <prefer>
            <family>Serif</family>
            <family>zhSerif</family>
        </prefer>
    </alias>

    <!-- 设置其他属性 -->
    <match>
        <!-- 设置合理的像素密度,确保pt与px之间能够合理转换 -->
        <edit name="dpi">
            <double>96</double>
        </edit>

        <!-- 设置等宽标记 -->
        <edit name="isDengKuan">
            <eq>
                <name>family</name>
                <string>Monospace</string>
            </eq>
        </edit>
    </match>

    <!-- 设置字体优先级 -->
    <!-- Default system-ui fonts -->
    <match target="pattern">
        <test name="family">
            <string>system-ui</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>sans-serif</string>
        </edit>
    </match>

    <!-- Default sans-serif fonts-->
    <match target="pattern">
        <test name="family">
            <string>sans-serif</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>Sans</string>
            <string>zhSans</string>
            <string>zzFailback</string>
        </edit>
    </match>

    <!-- Default serif fonts-->
    <match target="pattern">
        <test name="family">
            <string>serif</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>Serif</string>
            <string>zhSerif</string>
            <string>zzFailback</string>
        </edit>
    </match>

    <!-- Default monospace fonts-->
    <match target="pattern">
        <test name="family">
            <string>monospace</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>Monospace</string>
            <string>zhMonospace</string>
            <string>zzFailback</string>
        </edit>
    </match>

    <!-- 默认 emoji 字体。 -->
    <match target="pattern">
        <test name="family" qual="any">
            <string>emoji</string>
        </test>
        <edit name="family" mode="prepend" binding="strong">
            <string>zzSymbol</string>
        </edit>
    </match>

    <!-- 解决异体字,让 Noto CJK 在不同语言下采用不同的汉字变体。 -->
    <!-- zh-HK Sans -->
    <match target="pattern">
        <test name="lang">
            <string>zh-HK</string>
        </test>
        <test name="family">
            <string>zhSans</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans CJK HK</string>
        </edit>
    </match>

    <!-- zh-HK Serif -->
    <match target="pattern">
        <test name="lang">
            <string>zh-HK</string>
        </test>
        <test name="family">
            <string>zhSerif</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Serif CJK HK</string>
        </edit>
    </match>

    <!-- zh-HK Monospace -->
    <match target="pattern">
        <test name="lang">
            <string>zh-HK</string>
        </test>
        <test name="family">
            <string>zhMonospace</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans Mono CJK HK</string>
        </edit>
    </match>

    <!-- zh-TW Sans -->
    <match target="pattern">
        <test name="lang">
            <string>zh-TW</string>
        </test>
        <test name="family">
            <string>zhSans</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans CJK TC</string>
        </edit>
    </match>

    <!-- zh-TW Serif -->
    <match target="pattern">
        <test name="lang">
            <string>zh-TW</string>
        </test>
        <test name="family">
            <string>zhSerif</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Serif CJK TC</string>
        </edit>
    </match>

    <!-- zh-TW Monospace -->
    <match target="pattern">
        <test name="lang">
            <string>zh-TW</string>
        </test>
        <test name="family">
            <string>zhMonospace</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans Mono CJK TC</string>
        </edit>
    </match>

    <!-- ja Sans -->
    <match target="pattern">
        <test name="lang">
            <string>ja</string>
        </test>
        <test name="family">
            <string>zhSans</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans CJK JP</string>
        </edit>
    </match>

    <!-- ja Serif -->
    <match target="pattern">
        <test name="lang">
            <string>ja</string>
        </test>
        <test name="family">
            <string>zhSerif</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Serif CJK JP</string>
        </edit>
    </match>

    <!-- ja Monospace -->
    <match target="pattern">
        <test name="lang">
            <string>ja</string>
        </test>
        <test name="family">
            <string>zhMonospace</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans Mono CJK JP</string>
        </edit>
    </match>

    <!-- ko Sans -->
    <match target="pattern">
        <test name="lang">
            <string>ko</string>
        </test>
        <test name="family">
            <string>zhSans</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans CJK KR</string>
        </edit>
    </match>

    <!-- ko Serif -->
    <match target="pattern">
        <test name="lang">
            <string>ko</string>
        </test>
        <test name="family">
            <string>zhSerif</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Serif CJK KR</string>
        </edit>
    </match>

    <!-- ko Monospace -->
    <match target="pattern">
        <test name="lang">
            <string>ko</string>
        </test>
        <test name="family">
            <string>zhMonospace</string>
        </test>
        <edit name="family" binding="strong">
            <string>Noto Sans Mono CJK KR</string>
        </edit>
    </match>

    <!-- ========================== 第五部分 渲染阶段 =================================== -->

    <!-- 第一步,设置默认的渲染参数 -->
    <match target="font">
        <!-- 修整像素大小(小于10px的调整到10px,否则四舍五入到整数) -->
        <edit name="pixelsize">
            <if>
                <less>
                    <name>pixelsize</name>
                    <double>10</double>
                </less>
                <int>10</int>
                <round>
                    <name>pixelsize</name>
                </round>
            </if>
        </edit>

        <!-- 开启抗锯齿(smooth) -->
        <!-- 除非你的屏幕DPI奇高否则建议开启. -->
        <edit name="antialias">
            <bool>true</bool>
        </edit>

        <!-- 优先使用内嵌微调,同时默认开足微调 -->
        <!-- 字体微调的程度, 可选为 hintnone, hintslight (默认), hintmedium, hintfull. -->
        <!-- 简单来说,更高的 hinting 等级可以使字体更锐利,但同时也会损失更多的细节. -->
        <!-- 如果你的显示器的 DPI 高得不像话 (>=300), 那么就可以关闭 hinting, 因为字体会自然对齐像素. -->
        <edit name="hinting">
            <bool>true</bool>
        </edit>
        <edit name="autohint">
            <bool>false</bool>
        </edit>
        <edit name="hintstyle">
            <const>hintslight</const>
        </edit>

        <!-- LCD特征设置 -->
        <!-- rgba 是显示器使用的像素排列方式. 现代显示器基本都是用rgb排列 -->
        <!-- 关于lcdfilter, 因为我们在使用 FreeType2 自带的 Harmony 子像素渲染, 应该是不需要设置的. -->
        <edit name="rgba">
            <const>rgb</const>
        </edit>

        <edit name="lcdfilter">
            <const>lcddefault</const>
        </edit>

        <!-- 禁用内嵌点阵 -->
        <edit name="embeddedbitmap">
            <bool>false</bool>
        </edit>

        <!-- 禁用合成粗体 -->
        <edit name="embolden">
            <bool>false</bool>
        </edit>
    </match>
    <!-- 第二步,为没有原生斜体的字体使用合成斜体 -->
    <match target="font">
        <test name="slant" compare="eq">
            <const>roman</const>
        </test>
        <test name="slant" compare="not_eq" target="pattern">
            <const>roman</const>
        </test>
        <edit name="slant">
            <const>oblique</const>
        </edit>
        <edit name="matrix">
            <times>
                <name>matrix</name>
                <matrix>
                    <double>1</double>
                    <double>0.2</double>
                    <double>0</double>
                    <double>1</double>
                </matrix>
            </times>
        </edit>
    </match>
    <!-- 第三步,为没有原生粗体的字体使用合成粗体 -->
    <match target="font">
        <test name="weight" compare="less">
            <int>105</int>
        </test>
        <test name="weight" compare="more" target="pattern">
            <int>105</int>
        </test>
        <edit name="weight">
            <const>bold</const>
        </edit>
        <edit name="embolden">
            <bool>true</bool>
        </edit>
    </match>
    <!-- 第四步,标记"视觉大小"(原本的标称值)是否为奇数,为接下来修正等宽条件下的"标称大小"做准备 -->
    <match target="font">
        <edit name="isOddPx">
            <eq>
                <round>
                    <divide>
                        <plus>
                            <name>pixelsize</name>
                            <double>0.5</double>
                        </plus>
                        <double>2</double>
                    </divide>
                </round>
                <ceil>
                    <divide>
                        <plus>
                            <name>pixelsize</name>
                            <double>0.5</double>
                        </plus>
                        <double>2</double>
                    </divide>
                </ceil>
            </eq>
        </edit>
    </match>
    <!-- 第五步,修正合成粗体的"标称大小",尽力确保其"视觉大小"与原本的标称值一致 -->
    <match target="font">
        <test name="embolden">
            <bool>true</bool>
        </test>
        <!-- 标称大小=视觉大小-trunc((视觉大小+13.5)/25) -->
        <edit name="pixelsize">
            <minus>
                <name>pixelsize</name>
                <trunc>
                    <divide>
                        <plus>
                            <name>pixelsize</name>
                            <double>13.5</double>
                        </plus>
                        <double>25</double>
                    </divide>
                </trunc>
            </minus>
        </edit>
    </match>
    <!-- 第六步,在等宽条件下,为确保中西文对齐,进一步修正"标称大小"(也会影响"视觉大小") -->
    <match target="font">
        <test name="isDengKuan">
            <bool>true</bool>
        </test>
        <!-- 如果"视觉大小"是奇数 -->
        <test name="isOddPx">
            <bool>true</bool>
        </test>
        <!-- 那么上调为偶像素,因为Monospace在奇像素下总是大一级显示 -->
        <edit name="pixelsize">
            <plus>
                <name>pixelsize</name>
                <int>1</int>
            </plus>
        </edit>
    </match>
    <!-- 第六步续,进一步专门处理等宽条件下"标称大小"为11px,12px的合成粗体 -->
    <match target="font">
        <test name="isDengKuan">
            <bool>true</bool>
        </test>
        <test name="embolden">
            <bool>true</bool>
        </test>
        <test name="pixelsize" compare="more">
            <double>10.5</double>
        </test>
        <test name="pixelsize" compare="less">
            <double>12.5</double>
        </test>
        <!-- 统一调整为12px常规体,只有这样才能对齐 -->
        <edit name="pixelsize">
            <int>12</int>
        </edit>
        <edit name="embolden">
            <bool>false</bool>
        </edit>
        <edit name="weight">
            <int>80</int>
        </edit>
    </match>
    <!-- 最后,删除等宽标记与奇偶标记 -->
    <match target="font">
        <edit name="isDengKuan" mode="delete"></edit>
        <edit name="isOddPx" mode="delete"></edit>
    </match>

</fontconfig>

注意:配置完成后,请运行 fc-cache -fv 并重新登陆。

这个配置文件的特点在于,好像在写代码,它将 sans 等通用字体名当作变量,更换字体只需要改第一部分就可以了。同时使用 isDengKuan 引入了条件逻辑,非常巧妙。

我删除了参考配置中所有有关某特定程序(浏览器)的设置,个人认为没有必要。

流程:

  1. 在 scan 阶段,将自己选用的英文字体名称重命名为通用字体名称,将需要用的其他字体做类似操作。

  2. 在 patten 阶段,将匹配的字体的返回结果替换为通用字体名称。如果这里没有涵盖所有系统已安装的字体,会返回该字体原名称,可能导致最终结果不符合预期,因为通用字体的优先级比常用字体优先级低。例如在 Ubuntu 上我就遇到这个问题,毕竟 Ubuntu 默认安装非常全的字体。

  3. 在 font 阶段,修改渲染参数,设置 fontfeatures。Font configuration/Examples 有例子如何关闭 fontfeatures,例如关闭等宽字体Monaco的连字功能:

      <match target="font">
        <test name="family" qual="any">
          <string>Monaco</string>
        </test>
        <edit name="fontfeatures" mode="append">
          <string>liga off</string>
          <string>dlig off</string>
        </edit>
      </match>
    
DPI

DPI 即为 Dots per inch (每英尺点数), 可简单理解为显示器的像素密度。 由于在较低的像素密度下, 字形无法严格对齐像素格, 我们必须通过 hintingantialias (抗锯齿) 让字形在较低DPI的屏幕上有较好的观感。为了得到你的显示器的具体 DPI 数值, 可以使用 DPI Calculator 。或者按如下操作:

Find the DPI value for your screen

$ xdpyinfo | grep -B 2 resolution
screen #0:
  dimensions:    1366x768 pixels (340x191 millimeters)
  resolution:    96x96 dots per inch

How to calculate the right DPI value

  1. Get your screen size in millimeters by running the command:

    $ xrandr | grep -w connected
    eDP1 connected primary 1366x768+0+0 (normal left inverted right x axis y axis) 340mm x 190mm
    
  2. Convert it to centimeters. My values are 34 x 19.

  3. Convert centimeters to inches. Divide the values by 2.54. In my case, the values are as follows: 13.39in x 7.48in.

  4. Finally, divide your screen resolution values by inch values. In my case, the values are as follows: 1366/13.39 = 102.016430=~102. 768/7.48 = 102.673796=~102.

How to change the DPI value

  • No desktop environment/barebones window manager

    $ vi ~/.Xresources
    Xft.dpi: 102
    

    If your .Xresources file is not processed, add the following line to your startup file (e.g. .xinitrc or some window manager-specific file)

    xrdb -merge ~/.Xresources
    
  • Xfce

    The DPI can be set to what you want under Settings - Appearance - Font.

其他
  • XFCE 全局:Settings => Appearance => Fonts,Font 设置为 Sans Regular 和 Monospace Regular。然后 Hinting 设置为 Slight,DPI 设置为 102。之所以重复设置,是因为这里的设置会覆盖 fontconfig 的设置,fontconfig 大约在只有 WM 下才能设置一切吧。之前设置 Hinting 为 Full,结果 Hinting 过重导致 Telegram 英文字符间距不太均匀

  • xed:Edit => Preference => Font,设置为 Monospace Regular,字号 10。

  • Google Chrome 中的字体设置:用户要想 Google Chrome 听点话,需要在「设置」->「外观」->「自定义字体」里设置为 sans,serif,sans,monospace

  • Emoji 简介:它是一个日语词,e表示"絵",moji表示"文字"。连在一起,就是"絵文字"。Unicode 只是规定了 Emoji 的码点和含义,并没有规定它的样式。举例来说,码点U+1F600表示一张微笑的脸,但是这张脸长什么样,则由各个系统自己实现。

  • Typora: Preferences => Open Advanced Settings

  • Xfce Terminal: Settings => Xfce Terminal Settings => Appearance => Use system font

  • 苹果字体包:

  • analyze font files: only support OpenType fonts.

    sudo pacman -S lcdf-typetools
    otfinfo --info *.ttf | grep Subfamily
    
  • Preview Fonts:真的只能查看,把字体预览转成图片,不能看字体的 metadata,没什么用的。

    pacman -S fontpreview
    fontpreview /path/of/fonts
    
  • 有一些比较老的程序会忽略 Fontconfig 的设置,需要修改 Xresources。 ArchWiki 上提供的事例配置应该足够了。

Bluetooth

pacman -S bluez bluez-utils
lsmod | grep btusb
sudo systemctl enable --now bluetooth.service 
sudo usermod -a -G lp kurome # re-login

要自动化 bluetoothctl 命令,使用 echo -e "command1\ncommand2\n" | bluetoothctl

运行 bluetoothctl 交互命令。输入 help 来获取帮助。

  1. (可选操作)使用 select MAC_address 选择一个默认的蓝牙接收器。
  2. 使用命令 power on 打开蓝牙。蓝牙默认是关闭的,并且重启之后默认也会关闭。
  3. 使用命令 devices 获得要配对的设备的 MAC 地址。
  4. 如果设备没有出现在上一步的列表中,使用命令 scan on 去搜索发现所有可配对的设备。
  5. 使用命令 agent on 打开代理或者选择一个特定的代理:如果在 agent 命令后按下两次 tab 键,应该就能看到可用代理的列表。蓝牙代理用于管理蓝牙“配对码”。它可以回复外部发来的“配对码”,也可以主动发送。大部分情况下使用 default-agent 应该就足够了。
  6. 使用命令 pair MAC_address 配对设备(可用 tab 键补全 MAC 地址)。
  7. 如果配对设备不需要 PIN,那么你可能需要手动将设备添加到信任列表。使用命令 trust MAC_address
  8. 使用命令 connect MAC_address 建立连接。

最后,音频输出选择蓝牙耳机。

屏蔽网站

可以不需要插件的 一种简单的方式直接在 hosts 文件中添加一条记录即可

$ sudo vim /etc/hosts 
# forbidden website 
# 127.0.0.1 后面的网站可以替换成自己想要禁止访问的网页
127.0.0.1  www.csdn.net

但是上面依旧会显示在搜索结果中,同理 uBlock 等,只是无法访问,我想要的是不出现在搜索结果中,比如uBlacklist插件符合需求:

*://www.csdn.net/*
*://its401.com/*

KolourPaint

KDE 下免费、快速的图像编辑器,与Windows 7系统之前微软画图软件相似,但是添加了一些如支持透明度等的新特征。

packages out-of-date

First, you should flag the package out-of-date indicating details on why the package is outdated, preferably including links to the release announcement or the new release tarball.

You should also try to reach out to the maintainer directly by email. If there is no response from the maintainer after two weeks, you can file an orphan request.

zbar

Command-line QR-code decode: zbarimg

yay -S zbar
zbarimg "image-file-name.jpg"

zsh

安装 zfs

$ sudo pacman -S zsh zsh-autosuggestions zsh-syntax-highlighting zsh-completions autojump
$ chsh -l # 查看安装了哪些 Shell
$ chsh -s /usr/bin/zsh # 修改当前账户的默认 Shell
$ nano ~/.zshrc # 让插件生效
# Plugin
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
source /usr/share/autojump/autojump.zsh

可能需要:

  • 将在 ~/.bash_profile 所做的配置复制到 ~/.zsh_profile
  • 将在 ~/.bashrc 所做的配置复制到 ~/.zshrc

安装 powerlevel10k

git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/.powerlevel10k
echo 'source ~/.powerlevel10k/powerlevel10k.zsh-theme' >> ~/.zshrc

powerlevel10k 中包含许多特殊图标符号,需要与之兼容的字体。

.zshrc

# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
# Initialization code that may require console input (password prompts, [y/n]
# confirmations, etc.) must go above this block; everything else may go below.
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
  source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
fi

# Lines configured by zsh-newuser-install
HISTFILE=~/.histfile
HISTSIZE=1000
SAVEHIST=1000
bindkey -e
# End of lines configured by zsh-newuser-install
# The following lines were added by compinstall
zstyle :compinstall filename '/home/kurome/.zshrc'

autoload -Uz compinit
compinit
# End of lines added by compinstall

# Plugin
source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
source /usr/share/autojump/autojump.zsh

# CUSTOM BEGIN
## User specific environment
export PATH="$HOME/.local/bin:$PATH"
## Alias
alias lsptu="lsof -i | grep -i listen"
alias ls='ls --color=auto'
alias ll='ls -lav --ignore=..'   # show long listing of all except ".."
alias l='ls -lav --ignore=.?*'   # show long listing but no hidden dotfiles except "."
# CUSTOM END

source ~/.powerlevel10k/powerlevel10k.zsh-theme
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh

默认的 .bashrc

#
# ~/.bashrc
#

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

[[ -f ~/.welcome_screen ]] && . ~/.welcome_screen

_set_liveuser_PS1() {
    PS1='[\u@\h \W]\$ '
    if [ "$(whoami)" = "liveuser" ] ; then
        local iso_version="$(grep ^VERSION= /usr/lib/endeavouros-release 2>/dev/null | cut -d '=' -f 2)"
        if [ -n "$iso_version" ] ; then
            local prefix="eos-"
            local iso_info="$prefix$iso_version"
            PS1="[\u@$iso_info \W]\$ "
        fi
    fi
}
_set_liveuser_PS1
unset -f _set_liveuser_PS1

ShowInstallerIsoInfo() {
    local file=/usr/lib/endeavouros-release
    if [ -r $file ] ; then
        cat $file
    else
        echo "Sorry, installer ISO info is not available." >&2
    fi
}


alias ls='ls --color=auto'
alias ll='ls -lav --ignore=..'   # show long listing of all except ".."
alias l='ls -lav --ignore=.?*'   # show long listing but no hidden dotfiles except "."

[[ "$(whoami)" = "root" ]] && return

[[ -z "$FUNCNEST" ]] && export FUNCNEST=100          # limits recursive functions, see 'man bash'

## Use the up and down arrow keys for finding a command in history
## (you can write some initial letters of the command first).
bind '"\e[A":history-search-backward'
bind '"\e[B":history-search-forward'

##########################################################################################################
## Some generally useful functions.
## Consider uncommenting aliases below to start using these functions.
##
## October 2021: removed many obsolete functions. If you still need them, please look at
## https://github.com/EndeavourOS-archive/EndeavourOS-archiso/raw/master/airootfs/etc/skel/.bashrc

_open_files_for_editing() {
    # Open any given document file(s) for editing (or just viewing).
    # Note1:
    #    - Do not use for executable files!
    # Note2:
    #    - Uses 'mime' bindings, so you may need to use
    #      e.g. a file manager to make proper file bindings.

    if [ -x /usr/bin/exo-open ] ; then
        echo "exo-open $@" >&2
        setsid exo-open "$@" >& /dev/null
        return
    fi
    if [ -x /usr/bin/xdg-open ] ; then
        for file in "$@" ; do
            echo "xdg-open $file" >&2
            setsid xdg-open "$file" >& /dev/null
        done
        return
    fi

    echo "$FUNCNAME: package 'xdg-utils' or 'exo' is required." >&2
}

#------------------------------------------------------------

## Aliases for the functions above.
## Uncomment an alias if you want to use it.
##

# alias ef='_open_files_for_editing'     # 'ef' opens given file(s) for editing
# alias pacdiff=eos-pacdiff
##########################################################################################################

Emacs

学习Emacs有几条曲线

  1. 先会用基本的快捷键, 安装各种简单的插件, 抄各种各样的配置, 先体验一下Emacs的强大功能.

  2. 安装复杂插件, 被各种配置搞崩溃了, 越改挂的越厉害, 大部分人卡在这个阶段, 因为不会Elisp, 导致东拼西凑的方法对于复杂插件行不通

  3. 你搞不懂Emacs复杂的配置的原因是因为你不懂 Elisp 编程, 学习Elisp的方法: 老老实实读Emacs内置的 Elisp reference manual, 这么厚的手册怎么学?

    • 一页一页的挨着看, 一个API一个API的读, 不要跳过

    • 每个API, 都在 ielm 里面实践一下

    • 遇到不知道啥鬼用的API, 去Google或EmacsWiki上搜索一下, 看看别人怎么用这些API的?

  4. 如果你花了一个月耐下心读完 Elisp reference manual 以后, Emacs 90% 以上的代码你都可以看懂了, 继续折腾复杂插件, 知道 require, autoload, set-key, 各种 hook, defadvice 的在不同环境下的细微差别, 编程上知道 save-excursion, ignore-errors 这些风骚 macro 的用法. 这个阶段可以尝试手动来写一写复杂的配置了, 这个阶段你已经是 Emacs 高级用户了.

  5. 如果还不满足, 可以像我这样造点有趣的轮子: My Emacs Plugins 50 , 如果你自己会写Elisp插件, 你会发现Emacs其实是越用越简单的, 因为包括正则表达式, 语法高亮, 模式, 异步子进程, hook, overlay, advice 这些代码你写熟悉以后, 你会发现所有插件的唯一差别就是复杂度和想象力的区别, 不存在想得出来写不出来的东西.

  6. 到了这个阶段, 唯一可以让你学习到更多知识的就是去 IRC #emacs 和那些古老的黑客们交流, 或者去扒Github Emacser, 我知道很多日本牛逼哄哄的Emacs开发者都把插件放到 ftp 上 (比如当年的 color-moccur.el ), 学点 Google 语法就可以定向搜索. 这些人都是你会写Emacs插件后, 需要学习和进阶的榜样, 这时候你再看Elisp代码, 你的注意力会放在他们Elisp编程的一些细节上, 比如代码写的更简洁, 方法用的妙, 架构设计上等等, 这是完全不同阶段的探索体验.

  7. 如果你还不满足, 好好学习C语言, 然后再找个赚钱的工作, 把自己和家人照顾好. 业余时间直接用C或Elisp给Emacs底层做贡献, 把你的代码贡献固化到Emacs中, 然后你的名字可以像我一样写到 Emacs AUTHORS 里面去装逼: Emacs AUTHORS 146

如果你已经看到这里, 而且也做到上面的所有7点, 你自己的能力和精神境界都会很高了, 再也不会咋咋呼呼的吵着自己的技术要咋样咋样, 因为你会发现学的越多, 自己就是傻逼一个, 啥都不懂, 这辈子即使终身学习也学习不完, 哈哈哈哈.

上面就是我学习Emacs十多年的经验分享, 希望可以给同学们一点参考.

Questions

“signature is marginal trust”, “signature is unknown trust”, or “invalid or corrupted package”

如果您有任何错误,您可以清除 pacman 密钥环并重新开始:

sudo rm -fr /etc/pacman.d/gnupg
sudo pacman-key --init
sudo pacman-key --populate archlinux archlinuxcn endeavouros
sudo pacman -Syy archlinux-keyring archlinuxcn-keyring endeavouros-keyring
sudo pacman -Syyu
  • 记着将所有用到的仓库都添加,如上面分别是 archlinux、archlinuxcn、endeavouros 三个仓库
  • 千万不要运行 pacman-key refresh,超级慢,个把小时,实在没必要。

当 chrome 使用 GTK+ 主题的时候,无法调整窗口大小

enable “System title bars and borders”

有时候正确输入密码但是提示密码错误

问题来自一个经常需要特权并阻止我使用真实密码登录的流氓 cronjob。请参阅 pam_faillock 和 cron。

一般只需要等一会儿之后就好了。

Full Wayland Setup []

如果你遵循整个指南,到最后你将拥有:

  • Sway 一个平铺式窗口管理器。
  • Waybar 一个与 Polybar 非常相似的状态条。
  • Wofi 一个纯 GTK(也就是 Wayland)的可定制应用程序启动器。
  • Alacritty 一个现代化的终端,“又不是不能用”。
  • Wayland 中的 Firefox 和 Chromium,可以进行屏幕共享。
  • Emacs 通过全新的纯 GTK 内部结构在 Wayland 中完全运行。
  • 大多数 QT 应用程序在 Wayland 中运行。
  • 如果可以的话,将 Steam 游戏设置为考虑 Wayland。
  • (可选)通过 Fcitx5 的日语输入。

你还将学习如何确认一个应用程序是否在 Wayland 中运行,并了解 XWayland 和那些仍然需要它才能正常运行的主要程序。 虽然本指南是以 Arch Linux 为基础编写的,但它应该可以适应你所运行的任何的 Linux 发行版。好好享受吧,祝你好运!

注意:在继续之前,你可能希望在手机或第二台电脑上打开本指南,因为我们需要在整个过程中多次重启你的窗口管理器。

开始之前

Wayland 是 Linux 的下一代显示协议。你可能听说过 X(或 X11XOrg),但你可能不知道它的问题:年龄、性能、安全性和开发友好性。

甚至 Adam Jackson(X 的长期发布和管理者),也 呼吁采用 Wayland。 不过,X 已经很成熟了,过渡不会在一夜之间发生。Linux 系统中的许多核心应用都与它的生态系统紧密相连。

$ pacman -Qg xorg | wc -l
38

你可能会惊讶地发现,你几乎肯定已经安装了 wayland

$ pacman -Qi wayland
Name            : wayland
Version         : 1.19.0-1
Description     : A computer display server protocol
Architecture    : x86_64
URL             : https://wayland.freedesktop.org/
Licenses        : MIT
# ... etc.

幸运的是,Linux 生态系统向 Wayland 的过渡在这些年里一直在 稳步向前推进。 主要的 GUI 框架如 GTK 和 QT 完全支持它。 Ubuntu 21.04 将默认使用 Wayland 运行。 但我们可以不需要等待主要发行版的行动:今天就可以直接使用 Wayland。

你应该知道,有一些主要的应用程序并不(或不会 或不能)支持 Wayland。 像这样的程序仍然可以通过一个名为 XWayland 的独立的 X 实例在 Wayland 环境中运行。 这意味着向 Wayland 的过渡可以是渐进的:你不会无法使用旧的应用程序。

译者注:XWayland 本质上时 XOrg 的一个 Fork,可以在 Wayland 环境中使用 XOrg

在我们继续之前,还有一个好消息: 在 Wayland 中,你不需要像 picomcompiz 这样独立于窗口管理器的合成器程序。

解释一下?更少的活动组件,更少的配置管理,就可以实现终端透明化

前提条件

软件包

在 Arch Linux 上,运行下面的命令来安装本指南主要部分和 Wayland 兼容性所需的环境。

$ sudo pacman -S \
      sway alacritty waybar wofi \
      xorg-xwayland xorg-xlsclients qt5-wayland glfw-wayland

qt5-waylandglfw-wayland 分别为 QT5 和 GLFW 提供 Wayland 兼容 API 。

xlsclients 检测 XWayland

要获得当前通过 XWayland 运行的所有窗口的列表,请使用 xlsclients

$ xlsclients
archlinux  discord
archlinux  krita
archlinux  steam

这样,你就可以用各种应用程序快速测试你的 Wayland 配置。

Sway

Sway 是一个平铺式窗口管理器,是一个在 Wayland 环境下 i3 的替代品。 和它的 i3 一样,它也是用 C 语言编写的,因此速度非常快,资源开销也很小。 尽管 Sway 可以按原样读取 i3 的配置(即 /home/you/.config/i3/config),但我建议从一个默认配置开始,然后在你需要时复制特定的绑定。

首先,复制 Sway 的配置模板:

mkdir -p ~/.config/sway/
cp /etc/sway/config ~/.config/sway/

现在退出你所处的任何 桌面环境(DE) / 窗口管理器(WM),并回到你的基本登录终端。在这里,运行 sway,Sway 就会启动。恭喜你,你正在运行 Wayland!

Q: Sway: cannot start: libseat: Could not activate session: Permission denied

$ sudo pacman -S polkit

Q: prime import not supported sway in qemu-kvm

Sway (and wayland) requires a drm backend (a graphic card).

You should try with qxl, never had any issue. Add -vga qxl for the simplest configuration.

Q: Failed to pick cursor format

Does it work if you do export WLR_NO_HARDWARE_CURSORS=1 before you start sway? If it does, then your trackpad is working fine, it’s just your mouse cursor that is invisible. It’s a known bug with some graphics drivers.

A: 在 Qemu-Kvm 中,不要使用 vnc,使用 Ctrl+alt+G 来 Grab Input,否则会按键冲突,无法实验。

好了,我们不要庆祝的太早。你可能已经习惯了在 .xinitrc 中加入 exec i3 这样的行,然后用 startx 启动 X。现在不一样了! 从这里开始,一切都发生在我们的 Sway 配置中。说到这,下面是一些重点。

配置附加功能

这里是我的全部 Sway 配置。 其他方面,Sway 主要的文档是在它的 man pages 中记录的。如果有疑问,请先查看它们。 如果不行,你也可以参考 Sway Wiki

这里有一些有用的绑定,你马上就会需要,但以后可以自由改变。

  • 重新加载 Sway:Super+Shift+c (不会关闭正在运行的程序)
  • 退出 Sway:Super+Shift+e
  • 打开一个终端:Super+Return
  • 打开一个程序:Super+d

显示器设置

我有两台显示器:我的笔记本电脑在我的左边,而我主显示器在我的正前方。要让我的鼠标在显示器边界上自然移动,需要做以下工作。

output eDP-1 mode 2560x1440 position 0,0 scale 2
output HDMI-A-2 mode 1920x1080 position 1280,0

在确定第二个显示器(第二行中的 1280)使用的适当偏移量时,涉及到一些数学问题。更多信息见 man sway-output。 你可以使用 swaymsg -t get_outputs 来查看你所有显示器的正式名称和可用分辨率。

Gaps

i3-gaps 是一个流行的 i3 变种,允许窗口之间有间隙。幸运的是,这个功能已经包含在 Sway 中,可以通过在你的 Sway 配置中添加以下内容来激活。

# A 10-pixel border around every window.
gaps inner 10

# Removes the title bar of each window.
default_border pixel 3

你需要退出 Sway 一次,然后从你的登录终端重新运行它,这样的改变才会生效。

随机壁纸

虽然还没有整合到我自己的配置中,但 setwall 可以用来设置一个随机的背景图片。

setwall random ~/Pictures/backgrounds/ -c sway

译者注:setwall 是这篇文章的原作者自己写的工具

Alacritty

Alacritty 是一个强大的现代终端模拟器,具有理想的默认值。当用 Super+Return 打开一个新的终端时,它也是 Sway 的默认快捷键。 我使用 urxvt 多年,但最近切换到 Alacritty 后,我遇到的一些问题就消失了。

我对 Alacritty 的默认配置的唯一改变是背景的不透明度。在 /home/you/.config/alacritty/alacritty.yml 中。

background_opacity: 0.8

看, 透明的终端!

译者注: 新版本的 Alacritty 推荐设置

window:
opacity: 0.8

Waybar

Sway 的默认状态栏很好,但 Waybar 提供了更多的自定义功能。它还能在多个显示器上 “正常工作”,而不像 Polybar 那样需要自定义脚本。

要使用 Waybar 而不是默认 bar ,请注释掉你的 Sway 配置中靠近结尾的 bar 部分,并在其位置上添加以下内容。

bar {
  swaybar_command waybar
}

Waybar Wiki 有很多配置的例子,这里是我自己的 Waybar 配置, 以及 自定义的 CSS 样式

在调整了你的 Waybar 配置后,像往常一样通过 Super+Shift+c 刷新 Sway,就可以刷新你的 Waybar 了。

Wofi

默认情况下,Sway 使用 dmenu 来打开程序,但令人惊讶的是,它的用户界面在 XWayland 中运行。 有 许多可用的替代品,我选择了 Wofi

这是我设置的外观, 但由于它都是 CSS,所以你可以 自由地进行试验!

请注意,你需要在你的 Sway 配置中加入以下内容。

set $menu wofi --show=drun --lines=5 --prompt=""

这有几种不同的提示模式。 drun 只匹配并显示那些在你的机器上有 Desktop 条目的程序(就是有 .desktop 文件的程序),而不是你的 PATH 上的所有程序。 事实上,不这样做会产生性能问题,是一个已知的问题。

主要应用程序

大多数应用程序,如果在 GTK 或 QT 上运行,都有自动的 Wayland 支持,不需要进一步配置。一些特定的程序需要进行调整,我们将在下面讨论。

目前有一些资源要求你需要设置 GTK 和 QT 的特定环境变量才能使用 Wayland,但 我发现这不是真的

Firefox

在 Firefox 的 about:support 页面上有一个名为 Window Protocol 的字段,告诉我们它是通过哪个协议运行的。 如果还在 X11 上,这个字段就会显示 X11。如果通过 Sway 而没有下面的调整,你应该看到 xwayland。 用 xlsclients 进行的快速测试也会发现,Firefox 还没有通过 Wayland 原生运行。让我们来解决这个问题。

MOZ_ENABLE_WAYLAND 环境变量设为 1 ,我在我的 Fish 配置中设置了以下内容(其他 shell 的用户也需要类似的内容)。

set -x MOZ_ENABLE_WAYLAND 1

退出 Sway 并完全注销一次。 一旦重新登录并重新打开 Sway,这个变量的变化应该已经传播到了所有重要的地方。 现在,如果你通过 Wofi 再次打开 Firefox,并检查 about:support,你应该发现已改变。

Chromium

Chromium 的转换要简单一些。在 /home/you/.config/chromium-flags.conf 中,添加以下几行。

--enable-features=UseOzonePlatform
--ozone-platform=wayland

重新启动 Chromium,这样就可以了。你可以用 xlsclients 来确认。

Emacs

是的,Emacs 可以纯粹地在 Wayland 中运行。你们中的一些人可能会说。

但是 Emacs 并不是一个真正的 GTK 应用程序!

是的,这曾经是真的。从 2021 年初开始,Emacs 可以用 “纯 GTK” 的内部结构构建,使其完全兼容 Wayland。 这项功能将在 Emacs 28 中实现(截至本文撰写时尚未发布),但幸运的是, 有一个 AUR 包 可以跟踪 Wayland 开发分支,并提供一个预构建的二进制文件。 我们可以用 AURA 这样的工具来安装它。

译者注:AURA 也是这篇文章的原作者写的,中文社区的用户可能更习惯使用 yay

sudo aura -Axa emacs-gcc-wayland-devel-bin

注意,这个软件包 Provides: emacs,所以它将取代你所安装的任何其他 Emacs 软件包。

Steam and Gaming

Among Us 这样的 Proton games 可以按原样运行, 因为它们在高度修改的 Wine/dependency 环境中运行,而这种环境对每个游戏都是已知的。 Among Us 对 Sway 中的窗口大小调整和重新定位反应良好。

对于像 Half-life (old)、 Trine 2 (graphics heavy) 和 Tabletop Simulator(modern toolchain)这样的原生游戏, 我不得不将环境变量 SDL_VIDEODRIVER 设为 x11。否则它们就不能正常启动。

来自 Arch Wiki:

注意: 许多专有游戏都捆绑了旧版本的 SDL,它们不支持 Wayland,如果你设置 SDL_VIDEODRIVER=wayland,可能会完全崩溃。

甚至 Stellaris 也需要 x11 才能工作。

如果你不想把所有 SDL 的使用强制到 X11,你也不必这样做。Steam 允许我们为每个游戏设置特定的环境变量。 要设置这个,右键单击一个游戏,并访问其 Properties。在 GENERAL $ LAUNCH OPTIONS 中,输入SDL_VIDEODRIVER=x11 %command%,你的游戏应该可以运行了。

所以重申一下,这里是我在 Fish 中设置的环境变量。

set -x SDL_VIDEODRIVER 'wayland'

而我在 Steam 中根据具体情况将其覆盖为 x11

Signal

在 2021 年 5 月初,Signal 发布了 5.1.0 版本,该版本使用与 Wayland 兼容的 Electron。 不幸的是,Arch 软件包 signal-desktop 还没有默认在这种模式下运行,所以手动激活是必要的。在命令行中。

signal-desktop --use-tray-icon --enable-features=UseOzonePlatform --ozon-platform=wayland

或者如果你通过启动器运行 Signal,我们可以编辑软件包提供的 .desktop 文件来尊重这些选项。 在 /usr/share/applications/signal-desktop.desktop 中,修改 Exec 一行,使其成为以下内容。

Exec=signal-desktop --use-tray-icon --enable-features=UseOzonePlatform --ozone-platform=wayland -- %u

类似的策略也适用于其他至少使用 12 版的 Electron 应用程序。

Other Settings

如果这里的章节对你不适用,请随意跳过。

Keyboard Layouts

我在打字时使用 Colemak 布局,所以我在我的 Sway 配置里有以下内容。

input * {
  xkb_layout "us"
  xkb_variant "colemak"
}

不幸的是,似乎有 一个奇怪的 Bug,在某些窗口中布局会突然切换回 qwerty。 我注意到以下症状:当一个终端被打开时,最左边的 XWayland 窗口会切换回 qwerty。 我发现有两个办法可以解决这个问题。

  • 尽可能多地使用纯 Wayland 应用程序,或者。
  • 安装一个IME(Input Method Editor),例如用于输入 non-ASCII 语言(见下文)。

日语输入法

Sway 已经非常接近对切换输入法的一流支持(见 Sway#4740Sway#5890Sway#4932)。 目前,这里有一个通过 dbus 工作的设置,允许我们在 除 Alacritty 之外的所有 Wayland 和 XWayland 窗口中改变方法和输入日文。

首先,安装这些软件包。

sudo pacman -S fcitx5 fcitx5-configtool \
  fcitx5-gtk fcitx5-mozc fcitx5-qt

然后在 /etc/environment 中添加以下内容:

GTK_IM_MODULE=fcitx
QT_IM_MODULE=fcitx
XMODIFIERS=@im=fcitx

然后把这个放到你的 Sway 配置中:

exec_always fcitx5 -d --replace

现在重新启动你的电脑。

希望你现在能在你的 Waybar 托盘中看到一个键盘图标。要配置 fcitx5,请打开 fcitx5-configtool

你会看到,我特别将我的英文键盘设置为 Colemak,并从右边的列表中添加了 Mozc。 勾选 Global Options 标签,设置你的方法切换键绑定。 之后,点击 Apply,你现在应该可以切换输入法并输入日语了。 如果键盘绑定不起作用,你也可以通过点击 Waybar 托盘上的图标来切换方法。

译者注:对于中文用户,就是把 Mozc 换成 Rime

屏幕共享

在 Firefox 和 Chromium 中,通过 Pipewire 和一些辅助包可以实现屏幕共享,尽管目前我们只能共享整个屏幕,而不是单个窗口。要继续进行,首先安装以下软件包。

sudo pacman -S xdg-desktop-portal-wlr libpipewire02

后者只对 Chromium 是必要的。 现在重新启动你的电脑。

让我们先用 Mozilla 的 gum test page 测试一下 Firefox。 当浏览器提示你选择窗口时,选择 Use operating system settings

你会注意到你的光标发生了变化;xdg-desktop-portal-wlr 正在期待你选择一个显示器来共享。点击一个,屏幕共享应该开始。

对于 Chromium,我们需要激活一个功能标志,让 Chromium 与 Pipewire 对话。 首先访问 chrome://flags,然后找到并启用 WebRTC PipeWire support。这就是了!

如果你在使用这些浏览器时遇到问题,请查看 XDPW FAQ

XWayland 和不兼容

您还知道其他不兼容的情况吗?请让我知道。

Krita

数字艺术程序 Krita 是一个在 QT5 中运行的很棒的应用程序,但由于某些硬件支持不成熟的原因(对于手写板等), 它不支持 Wayland,因此总是在 XWayland 中运行。

Electron Apps

从 2021 年 5 月起,由于 Signal 和 VSCode 升级到 Electron 12,它们现在可以在 Wayland 中运行。

而其他 Electron Apps,如 Discord 和 Slack,则必须在 XWayland 中运行,直到他们能够升级。

社区提示

KWin 用户

感谢 flying-sheep 提供的这个提示。

对于使用 KWin 的人来说:你可以显示一个窗口,帮助你使用识别 XWayland 窗口。

qdbus org.kde.KWin /KWin org.kde.KWin.showDebugConsole

Polkit

感谢 Aaron Wiedemer 提出的以下建议。

一些应用程序有时需要权限,例如,软件管理器需要权限来启动更新,但只是搜索软件包不需要额外权限。 这些应用程序会弹出一个小盒子,要求输入密码。这需要一个不由 sway 启动的守护程序,所以我们需要用我们的 sway 配置自动启动一个。

Polkit 客户端有很多选择。 例如,polkit-gnome 没有依赖性,可以通过以下方式在 sway 中启动:

exec_always /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1

WM

Window manager recommendation?

i3 (sway for Wayland) is the easy peasy starter kit. I like Bspwm, it’s one big bash script basically to get configs going. Then there’s the more challenging one that you can pick based on your preferred programming language: Awesome (lua), Xmonad (haskell), DWM (C), Qtile (python)

awesome wm, probably the most extensive wm, for one you can load whole libraries into it like xmonad, but using lua is do much easier than haskell, tho the api docs could get an overhaul as they are kind of ugly when giving code examples going straight from 1 to 10.

常见的窗口管理相关的工具

合成器

  • Compton - Compton 是一款独立的合成管理器,适合同没有原生提供合成功能的窗口管理器一同使用。
  • Gamescope - Gamescope 是一款微合成器,提供一个带有独立输入,分辨率和刷新率的沙盒 Xwayland 桌面。
  • Sway - Sway 是平铺 Wayland 合成器和 X11 下 i3 窗口管理器的新替代。
  • Xcompmgr - Xcompmgr 是一个简单的合成管理器,能够渲染下拉阴影,使用 transset 工具的话,还可以实现简单的窗口透明。

叠加式窗口管理器

  • 2bwm - 快速的浮动窗口管理,有两个特殊边界,基于 XCB 库,由 mcwm 衍生。
  • Blackbox - 快速,轻量化的 X 窗口系统窗口管理器,没有那些烦人的库依赖。
  • Fluxbox - 基于 Blackbox 0.61.1 代码的 X 窗口管理器。
  • Openbox - 高度可配置,带有可扩展标准支持的下一代窗口管理器。

平铺式窗口管理器

  • Bspwm - bspwm 是一个平铺式窗口管理器,将窗口以二叉树的叶结点的方式展现。
  • Herbstluftwm - 使用 Xlib 和 Glib 的手工平铺式窗口管理器。
  • i3 WM - 更好的平铺及动态窗口管理器。完全重写。目标平台是 GNU/Linux 和 BSD 操作系统。
  • i3-gaps - i3-gaps 是拥有更多功能的 i3。
  • Pop!_OS Shell - Pop Shell 是基于 GNOME shell 的窗口管理器,键盘驱动,自动平铺。
  • Qtile - qtile 是一款全功能,可 hack 的平铺窗口管理器,使用 Python 编写和配置。

动态窗口管理器

  • awesome - 高度可配置,下一代 X 框架窗口管理器。
  • dwm - X 动态窗口管理器。它以平铺,单片镜以及浮动布局的方式管理窗口。
  • spectrwm - 小型动态平铺 X11 窗口管理器。主要受 xmonad 和 dwm 启发。
  • xmonad - 动态平铺 X11 窗口管理器,用 Haskell 编写和配置。

长时间日常使用动态窗口管理器之后发现,相比与传统的DE型桌面,这种WM型窗口管理方式确实能很大程度上提高工作效率。

DE和WM的区别

首先说明一下我即将提到的东西是什么。

  • DE:Desktop Environment
  • WM:Window Manager

桌面环境是一个相对完整的概念,一般情况下DE包含WM,比如Gnome3的默认WM是Mutter,KDE5的默认WM是KWin。主流的桌面环境都提供了大量自带的桌面组件来构成其完整的桌面体验。

窗口管理器只是管理出现在你的桌面上的各种窗口的组件,具体的管理内容包含比如:窗口堆叠方式,窗口移动规则等。大多数人接触到的是堆叠式窗口管理器,一个窗口可以叠放在其他窗口之上,调整窗口的主要方式是鼠标。我即将介绍的dwm(Dynamic Window Manager)属于动态窗口管理器,可以自定义不同窗口的出现规则如平铺或者堆叠,主要的调整窗口的方式是键盘。

大多数窗口管理器只提供管理窗口的逻辑和人机交互的处理,并不自带其他组件如程序启动器、终端等软件,因此只运行一个窗口管理器理论上是要比运行一个完整的桌面环境更加节约资源,也有更好的可伸缩性。

实际上WM这个概念是应用了奥卡姆剃刀原理,把那些看似有点用但实际上多余的功能全部剔除,只留下你真正需要的和最常用的功能并加以强化,Less is More.

通用配置

因为一个可独立运行的窗口管理器默认不携带任何多余的软件,所以为了满足日常使用需要进行一定程度的自定义配置,下面给出一下通用的配置:

Xorg & font

sudo pacman -S xorg-server xterm xorg-xinit 
sudo pacman -S noto-fonts noto-fonts-cjk noto-fonts-emoji
xinitrc

~/.xinitrc 可以方便的启动依赖 X 的程序,并在 X 启动时设置环境变量。如果用户主目录中存在 .xinitrcstartxxinit 会执行此文件。如果不存在,startx 会执行默认的 /etc/X11/xinit/xinitrc。这个文件默认启动 TwmXterm.

要设置窗口管理器或桌面环境,先通过复制创建默认文件:

cp /etc/X11/xinit/xinitrc ~/.xinitrc

根据示例文件修改可以保留一些默认行为,例如会引用 /etc/X11/xinit/xinitrc.d 中以 .sh 结尾的脚本。

然后编辑 ~/.xinitrc

$ vim ~/.xinitrc
#!/bin/bash
xscreensaver &
xsetroot -cursor_name left_ptr &
exec openbox-session

~/.xinitrc 中应该只有 一个 未注释掉的 exec 行,而且 exec 行必须位于配置文件的末尾。exec 后面的所有命令只有窗口退出后才会被执行。在窗口管理器前启动的命令,例如屏保和壁纸程序,必须自行 fork 后台进程或用&在后台启动, 否则启动程序会等待它们退出才会启动窗口管理器或桌面环境。使用 exec 作为前缀会替换当前的进程,这样进程进入后台时 X 不会退出。

某些程序,比如 xrdb,不应该被 fork. 使用 exec 前缀时,程序将会用窗口管理器进程替换脚本进程,所以即使进程进入后台 X 也不会退出。

xserverrc

xserverrc 文件是一个启动 X server 的 shell 脚本。如果存在 ~/.xserverrcstartxxinit 都会执行这个文件。如果文件不存在,startx 会使用 /etc/X11/xinit/xserverrc.

为了保持logind的 authenticated session 会话,并防止通过切换终端绕过屏幕锁定器, Xorg必须在发生登录的同一虚拟终端上启动。因此建议在~/.xserverrc 中指定 vt$XDG_VTNR:

$ vim ~/.xserverrc
#!/bin/sh

exec /usr/bin/Xorg -nolisten tcp "$@" vt$XDG_VTNR
在登录时自动启用 X

如果使用Bash, 编辑 ~/.bash_profile,加入如下内容。如果文件不存在,从 /etc/skel/.bash_profile 复制一个。

如果使用 zsh,则编辑 ~/.zprofile.

$ vim ~/.bash_profile
if [[ ! $DISPLAY && $XDG_VTNR -eq 1 ]]; then
  exec startx
fi

如果希望在 X 会话终止时保持登入状态,删除 exec.

Automatic login into Xorg without display manager
yay -S xlogin-git
sudo systemctl enable xlogin@username

常用软件的推荐

注意:大部分软件需要根据所使用的 wm 添加配置才能使用,具体参照他人的配置

  • Font: nerd-fonts-ibm-plex-mono, nerd-fonts-fira-code, nerd-fonts-jetbrains-mono, ttf-sarasa-gothic(chinese only), infinality(字体渲染美化)
  • Launcher:dmenu, rofi
  • Terminal: alacritty, kitty, st, wezterm, termite
  • Editor: neovim, neovide&glrnvim, doom emacs, vscode, sublime-text-nightly
  • Compositor: picom-jonaburg-git, picom
  • Shell: fish, zsh
  • PDF reader: zathura, atril
  • Player: mpd&ncmpcpp, deadbeaf-git
  • Notification daemon: dunst&libnotify, notification-daemon
  • Volume notificator: xob, volumeicon
  • Screen locker: i3lock-color&betterlockscreen, xautolock, xscrennserver
  • System monitor: conky, btop
  • Theme controler: xsettingsd, lxappearance, qt5ct
  • Gtk themes: Light: Orchis-light, Dark: Nordic
  • Qt themes: use qt5ct and qt5-styleplugins to set qt theme follow gtk2 theme
  • Icon theme: Papirus
  • Touchpad: libinput, ibinput-gestures, xdotool
  • Fetch tool: macchina, rxfetch
  • Automounter: udiskie
  • Trash: glib2, gvfs(gui)
  • 壁纸设置器:feh, nitrogen(gui)
  • 文件管理器:thunar, nemo(cli), ranger(cli), pcmanfm(轻量级文件管理器)
  • 电源管理器:xfce4-power-manager, cbatticon
  • 截图工具:flameshot, scrot
  • 输入法:fcitx5
  • 科学上网工具:qv2ray
  • 剪切板管理器:copyq
  • 云同步工具:nutstore(坚果云), dropbox
  • 护眼:redshift
  • 屏幕亮度调节器:light, xorg-xbacklight, brightnessctl

关于平铺还是浮动

个人建议多数窗口平铺,以下窗口默认浮动:

virtualbox, lxappearance, qq, gpick, qalculator, slickpicker

关于GTK主题和QT主题

我建议让QT程序使用GTK主题,除上文提到的qt5ct再安装qt5-styleplugins

在qt5ct中设置风格为gtk2

配置

从上到下,基本上 star 越多,就越完善,越容易安装与使用:

dwm

dwm与其他wm的区别

前面已经说明了dwm与堆叠式窗口管理器的区别,下面说明一下dwm与其他平铺&动态窗口管理器的区别。

dwm使用C语言编写,使用修改源代码的方式来进行配置。说到源代码可能有些同学就有点害怕,其实修改源代码也很简单,因为有现成的例子可以复(bai)用(piao)嘛!通过直接修改源代码理论上可以确保你的dwm能满足你所有的需求,同时也没有多余的,你不需要的功能。

相比之下,另外一个相当流行的动态窗口管理器i3wm也是使用C语言编写,但是通过修改其配置文件来配置,这相当于i3wm向你暴露可以被配置的接口来满足你的需求,但是这样的配置方式缺点在于你不能向其添加你需要的需求,只能向上游提交issue来完成enhancement。

具有和dwm相似配置能力的窗口管理器是xmonad,它也是通过直接修改源代码的方式来调整配置,但是因为使用haskell编写的所以需要安装相当多的依赖,此外其运行效率也不及dwm。

dwm安装

dwm

$ sudo pacman -S libxinerama
$ wget https://dl.suckless.org/dwm/dwm-6.3.tar.gz
$ tar xpvf dwm-6.3.tar.gz
$ mv dwm-6.3 ~/.dwm
$ cd .dwm
$ make clean install
$ vim ~/.xinitrc
#!/bin/bash
exec dwm

st

wget https://dl.suckless.org/st/st-0.8.5.tar.gz

如果使用的终端模拟器不是st,需要修改config.def.h文件中的以下代码:

/* 将其中的st修改为安装的终端模拟器 */
static const char *termcmd[] = {
     "st", NULL};

dmenu

wget https://dl.suckless.org/tools/dmenu-5.1.tar.gz

dwm配置

配置dwm主要需要修改的文件是config.def.h,主要的配置方式是使用被人写好的patch加必要时候的手动修改。dwm的patch都在其官方网站

具体过程是这样的:

  1. 找到你需要的patch并下载
  2. 将其移动到~/.dwm目录下
patch < dwm*.diff

之后会输出这个patch过程的详细情况,一般情况不会出现问题

需要注意的是要一个一个来

出现问题之后需要对照如:config.def.h.rej, dwm.c.rej等后缀为rej的文件来手动修改对应的没有rej后缀的源文件

打开之后rej文件中行首标有+的行是需要添加的行,标有-的行是需要删除的行

所有更改都修改完毕之后重新编译安装,执行:

rm -f ./config.h && sudo make clean install

Starting

Select Dwm from the menu in a display manager of choice. Alternatively, to start dwm with startx append exec dwm to ~/.xinitrc and prepend other programs to execute them as well, for example:

redshift -O3500; xset r rate 300 50; exec dwm

字体和左侧状态栏图标

$ sudo pacman -S ttf-nerd-fonts-symbols-mono
$ nvim ~/.local/applications/dwm/dwm-6.2/config.h
/*设置dwm采用图标字体*/
static const char *fonts[] = {"Symbols Nerd Font:size=14"}; 
/*图标对应的ASCLL码:https://www.nerdfonts.com/cheat-sheet*/
static const char *tags[] = {"\ue795","\ufc6e","\ue235","\uf308"};  

Statusbar

The information that you want dwm to show in the statusbar should be defined with xsetroot -name "" command in ~/.xinitrc or ~/.xprofile (if you are using a display manager). For example:

# Statusbar loop
while true; do
   xsetroot -name "$( date +"%F %R" )"
   sleep 1m    # Update time every minute
done &

# Autostart section
pcmanfm & 

exec dwm

Conky statusbar

Conky can be printed to the statusbar with xsetroot -name:

(conky | while read LINE; do xsetroot -name "$LINE"; done) &
exec dwm

自动更换壁纸

创建壁纸目录

mkdir -p ~/pictures/wallpapers

安装图片查看器feh,编写更换壁纸的配置文件

$ pacman -S feh
$ nvim ~/.dwm/dwm-wallpaper.sh
#!/bin/bash

while true
do
  feh --recursive --randomize --bg-fill ~/pictures/wallpapers/
  sleep 5m
done

启动DWM时,自动执行壁纸配置文件

$ nvim ~/kler_profiles/.dwm/autostart.sh
#!/bin/bash

/bin/bash ~/kler_profiles/.dwm/dwm-wallpaper.sh &

picom

picom是Xorg的独立合成器,适用于不提供合成功能的窗口管理器(例如 i3,dwm) 他可以给窗口设置淡入淡出、半透明、阴影等视觉效果。

推荐补丁

透明补丁:alphasystray.diff

临时小窗口:dwm-scratchpad-6.2.diff

隐藏空标签:dwm-hide_vacant_tags-6.2.diff

窗口间距:dwm-vanitygaps-20190508-6.2.diff

自动启动脚本:dwm-autostart-20161205-bb3bd6f.diff

窗口全屏:dwm-actualfullscreen-20191112-cb3f58a.diff

状态栏显示多个窗口信息:dwm-awesomebar-20191003-80e2a76.diff

awesome

awesome是一个高度可配置的、用于Xorg的下一代框架窗口管理器。它非常快而且可扩展。它主要针对的是高级用户、开发人员和任何处理日常计算任务的人,他们希望对其图形环境进行精细的控制。

安装

Install the awesome package. The development version is awesome-gitAUR, which is considered unstable and may have a different configuration API.

$ pacman -S awesome
$ vim ~/.xinitrc
#!/bin/bash
exec awesome

自定义配置

Awesome提供了一个设置文件 rc.lua ,把它拷贝到~/.config/Awesome/里。

mkdir -p ~/.config/awesome/
cp /etc/xdg/awesome/rc.lua ~/.config/awesome/
  • 登录后顶部有一条类似Windows任务栏的东西,叫做状态栏(Status Bar)
  • 状态栏的最左侧是Awesome的图标,点击它将会打开一个小菜单,这就是Awesome的主菜单
  • Awesome 图标的右侧可以看到阿拉伯数字1-9,这些叫做标签(Tag),类似GNome或者KDE下的虚拟桌面。
  • 在状态栏最右侧是布局切换器,布局(Layout)是根据屏幕上的可用空间来调整窗口位置、尺寸的方式。Awesome支持多种布局。

每次修改完使用 Mod4 + ctrl + R 重新加载配置。

Some good examples of rc.lua would be as follows:

定制布局和标签

定制布局

如果希望改变默认的布局和修改布局的切换顺序,可以通过修改 rc.lua 文件来实现。找到像这样的一段代码:

layouts =
 {
     awful.layout.suit.floating,
     awful.layout.suit.tile,
     awful.layout.suit.tile.left,
     awful.layout.suit.tile.bottom,
     awful.layout.suit.tile.top,
     awful.layout.suit.fair,
     awful.layout.suit.fair.horizontal,
     awful.layout.suit.spiral,
     awful.layout.suit.spiral.dwindle,
     awful.layout.suit.max,
     awful.layout.suit.max.fullscreen,
     awful.layout.suit.magnifier
 }

这是所有Awesome提供的布局方案,并且是按切换顺序排列的。可以通过调整顺序来改变实际的布局切换顺序。如果不喜欢其中的一些布局方案,你可以把相应的代码给去掉。比如,对于我而言,全屏、放大和螺旋式布局都是不常用的,并且我希望第一个布局是平铺,而不是浮动,因此可以改成:

layouts =
{
    awful.layout.suit.tile,
    awful.layout.suit.tile.left,
    awful.layout.suit.tile.bottom,
    awful.layout.suit.tile.top,
    awful.layout.suit.fair,
    awful.layout.suit.fair.horizontal,
    awful.layout.suit.floating,
    awful.layout.suit.max
}

完成后保存修改,然后按 Mod4 + Control + r 重启 Awesome看看变化。

浮动窗口

有些程序的窗口不适合采用平铺的方式,比如Firefox的下载窗口,或者Gimp的图层窗口、工具栏窗口等,这时候我们希望将这样的窗口设置为浮动:

-- 需要自动设置为浮动的程序
-- 只需要把你想要设置为浮动窗口的程序的Instance或者class按照下面的格式写进去就行
-- 了。在awesome下用Mod4 + Ctr + i就可以看到当前程序的instance和class名字
-- { { { Rules
awful.rules.rules = {
   -- All clients will match this rule.
   {rule = {},
    properties = {border_width = beautiful.border_width,
                  border_color = beautiful.border_normal,
                  focus = true,
                  keys = clientkeys,
                  buttons = clientbuttons}},
   {rule = {class = "MPlayer"},
    properties = {floating = true}},
   {rule = {class = "Smplayer"},
    properties = {floating = true, tag = tags[1][6]}},
   { rule = { class = "pinentry" },
     properties = { floating = true } },
   { rule = { class = "gimp" },
     properties = { floating = true } },
   {rule = {class = "Firefox"},
     properties = {tag = tags[1][1]}},
   {rule = {class = "Firefox", name = "Download"},
     properties = {floating = true}},
   {rule = {class = "VirtualBox"},
     properties = {floating = true, tag = tags[1][2]}},
   -- Set Firefox to always map on tags number 2 of screen 1.
   -- { rule = { class = "Firefox" },
   --   properties = { tag = tags[1][2] } },
}
-- } } }
定制标签

只要你愿意,你可以为每一个标签命名,并为每一个标签设置默认布局。这是 rc.lua 里默认的标签设置:

-- { { { Tags
-- Define a tag table which hold all screen tags.
tags = {}
for s = 1, screen.count() do
    -- Each screen has its own tag table.
    tags[s] = awful.tag({ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, s, layouts[1])
end
-- } } }

现在我们可以改变每个标签的名字,并为每一个设置默认布局。参考下面这段代码:

-- { { { Tags
-- Define a tag table which will hold all screen tags.
tags = {
  names  = { "main", "www", "im", "gimp", "office", "music", "virtual", 8, 9 },
  layout = { layouts[1], layouts[8], layouts[8], layouts[7], layouts[1],
             layouts[7], layouts[8], layouts[1], layouts[1]
}}
for s = 1, screen.count() do
    -- Each screen has its own tag table.
    tags[s] = awful.tag(tags.names, s, tags.layout)
end
-- } } }

在这个例子中,我们还为前5个标签分别命名为"main", “www”, “im”, “gimp” 和"office", 你可以根据自己的喜好进行更改。另外,我们还使用 layouts来指定每一个标签的布局, “[]“里的数字就是我们刚刚 定制布局的时候设置的布局的顺序,例如[1] 就是第1个布局。

设置默认终端和编辑器

可以在 rc.lua 中修改下面的代码来设置默认的终端和编辑器:

terminal = "xterm"
editor = os.getenv("EDITOR") or "nano"

美化

更换主题

Beautiful是一个Lua库,它允许你使用外部文件来为awesome做主题,在不改变你的rc.lua的情况下,动态地改变你整个awesome的颜色和墙纸变得非常简单。

Awesome自带了三套主题: defaultskyzenburn

The default theme is at /usr/share/awesome/themes/default. Copy it to ~/.config/awesome/themes/default (optionally copy them all) and change rc.lua:

$ cp -av /usr/share/awesome/themes ~/.config/awesome/themes
-- beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua")
local theme_path = string.format("%s/.config/awesome/themes/%s/theme.lua", os.getenv("HOME"), "default")
beautiful.init(theme_path)

把其中的 default 更换成其他主题名字即可。

或者直接添加路径

beautiful.init("/usr/share/awesome/themes/default/theme.lua")

如果嫌主题太少,这里有很多PP的用户定制主题: Github主页,下载下来然后拷贝到~/.config/awesome/themes即可以使用。

更换背景图片

主题的 theme.lua 文件可以配置背景、字体等内容,改一下图片路径:

theme.wallpaper = themes_path .. "default/background.png"

我希望背景图片最大化但不要拉伸,所以我将原本的

gears.wallpaper.maximized(wallpaper, s, true)

改成了

gears.wallpaper.maximized(wallpaper, s, false)

同时背景设置还有其他的方法:

  • centered (surf, s, background, scale)
  • tiled (surf, s, offset)
  • fit (surf, s, background)

可以参考官方文档 或者 源码

修改主菜单

可以通过修改 rc.lua 的相应内容来定制主菜单的内容。下面是我的配置。里面的设置完全根据我个人口味,而且用了几个第三方的icon。因此代码仅供参考,不建议照搬:

-- { { { Menu
-- Create a laucher widget and a main menu
myawesomemenu = {
   { "manual", terminal .. " -e man awesome" },
   { "edit config", editor_cmd .. " " .. awesome.conffile },
   { "restart", awesome.restart },
   { "quit", awesome.quit }
}

-- 创建一个favorite子菜单
myfavoriteapps = {
   { "Terminal", terminal },
   { "Firefox", "firefox" },
   { "QQ", "qq2012"},
   { "XMind", "/usr/local/xmind/xmind" },
   { "Synaptic", "gksudo synaptic" },
   { "Transmission", "transmission-gtk"},
   { "Software-Center", "gksudo software-center" },
   { "Update-Manager", "gksudo update-manager" },
   { "JabRef", "sh /home/ehome/JabRef.sh" }
}

mymainmenu = awful.menu({ items = {
{ "awesome", myawesomemenu, beautiful.awesome_icon },
{ "Debian", debian.menu.Debian_menu.Debian , theme.deb_icon },
-- 添加Favorite菜单,并将icon设置为theme.fav_icon
{ "Favorite", myfavoriteapps, theme.fav_icon },
{ "Home", "dolphin" , theme.home_icon },
-- 添加Emacs菜单项
{ "Emacs", "emacs" },
-- 添加Chrome菜单项
{ "Chrome", "google-chrome" },
-- 添加一个关机对话框
{ "Log out", "/usr/local/shutdown.sh" }
 }
})

mylauncher = awful.widget.launcher({
image = image(beautiful.awesome_icon),
 menu = mymainmenu })
-- } } }
GTK & QT 主题

安装工具

pacman -S lxappearance qt5ct qt5-styleplugins
export QT_QPA_PLATFORMTHEME=qt5ct
  • 下载解压 Gtk themes Nordic 到 ~/.themes

  • 下载解压 Icon theme Papirus 到 ~/.icons

lxappearance # 选择 theme
qt5ct # set qt theme follow gtk2 theme

插件

Awesome 里的插件指的是你可以在 wibox 上添加的小插件,通过使用插件(Widget),我们可以在状态栏上添加一些有用的信息,例如内存使用、CPU温度、电池状态,等等。

对于新手,可以使用其他用户创建的插件库,这些插件库集成了功能齐全的插件,因此免去了自己编写插件的工作。这类的插件库有很多,推荐的是 Vicious

创建插件

要创建插件,可以使用 widget() 函数,例如:

mysystray = widget({type = "systray"})
myicon = widget({ type = "imagebox" })
myicon.image = image(awful.util.getdir("config") .. "/myicon.png")
mytextbox = widget({ type = "textbox" })
mytextbox.text = "Hello, world!"
插件的类型

使用 widget() 函数创建插件的时候需要指定插件类型,Awesome自带的插件包括:

  • systray:系统托盘插件。Awesome默认已经添加,就在状态栏上位于日期和时间左侧的区域。对于一些支持最小化到系统托盘的程序,例如ibus、chrome、shutter等,它们的图标将被放置在这里。
  • imagebox:展示一张图片。常用来和textbox一起搭配使用,创建启动器、图标和分隔符。这里 收集了很多精美的图标。
  • textboxtextbox:插件用来显示一段文本,这是最常用的插件。例如,在创建插件部分我们已经创建了一个名为mytextbox的textbox插件,并且插件显示的文本内容是"Hello, World!"。要修改文本内容,可以直接修改 rc.lua文件中的相应代码,或在终端中使用下面命令:
echo "mytextbox.text = "Foo Bar!"" | awesome-client

Awesome还提供了计时器的API, 允许我们周期性的执行或更新插件。例如:

mytimer = timer({ timeout = 30 })
mytimer:add_signal("timeout", function() mytextbox.text = "Hello awesome world!" end)
mytimer:start()

textbox的文本属性(颜色、字体)是可以通过使用 Pango 标记语言来修改。最简单的例子是:

mytextbox.text = '<span color="white">Sacrebleu, I have seen a ghost!</span> '

要了解更多textbox的设置,例如改变背景颜色、边框、文本对齐等,可以参看Awesome的API文档

推荐:实用插件

awesome-wm-widgets 收集了丰富的插件:

音量控制插件

Awesome 自己不提供音量控制插件,如果未经设置,还会发现原本键盘上的多媒体按键都会失效,这是因为没有让 Awesome 绑定这些热键。下面实现一个音量控制插件,并且将音量控制绑定到多媒体键盘的音量加(XF86AudioRaiseVolume)、音量减(XF86AudioLowerVolume)、静音(XF86AudioMute)几个按键上。

首先实现一个音量控制插件,将它保存为 ~/.config/awesome/volume.lua:

-- Create a volume control

volume_widget = widget({ type = "textbox", name = "tb_volume",
                         align = "right" })

function update_volume(widget)
   local fd = io.popen("amixer sget Master")
   local status = fd:read("*all")
   fd:close()

   local volume = tonumber(string.match(status, "(%d?%d?%d)%%")) / 100
   -- volume = string.format("% 3d", volume)

   status = string.match(status, "%[(o[^%]]*)%]")

   -- starting colour
   local sr, sg, sb = 0x3F, 0x3F, 0x3F
   -- ending colour
   local er, eg, eb = 0xDC, 0xDC, 0xCC

   local ir = volume * (er - sr) + sr
   local ig = volume * (eg - sg) + sg
   local ib = volume * (eb - sb) + sb
   interpol_colour = string.format("%.2x%.2x%.2x", ir, ig, ib)
   if string.find(status, "on", 1, true) then
      volume = " <span background='#" .. interpol_colour .. "'>   </span>"
   else
      volume = " <span color='red' background='#" .. interpol_colour .. "'>
       M
      </span>"
   end
   widget.text = volume
end

update_volume(volume_widget)
awful.hooks.timer.register(1, function () update_volume(volume_widget) end)

然后编辑 rc.lua ,在开头引入这个插件:

-- Volume contrl
local volume_widget = require("volume")

在 wibox 中添加这个插件,例如放在系统托盘的右侧:

    -- Add widgets to the wibox
    s.mywibox:setup {
        layout = wibox.layout.align.horizontal,
        { -- Left widgets
          ...
        },
        s.mytasklist, -- Middle widget
        { -- Right widgets
          ...
          volume_widget
          ...
        },

最后绑定到几个多媒体键:

-- volume control
awful.key({ }, "XF86AudioRaiseVolume", function ()
             awful.util.spawn("amixer set Master 9%+") end),
awful.key({ }, "XF86AudioLowerVolume", function ()
             awful.util.spawn("amixer set Master 9%-") end),
awful.key({ }, "XF86AudioMute", function ()
             awful.util.spawn("amixer sset Master toggle") end),

这个插件将在状态栏放置一个亮度随音量大小改变的白色小方块,当系统处于静音状态时,图标将变为一个红色的 M 字符。

Vicious
pacman -S vicious

rc.lua 头部增加声明信息并添加 Date Widget:

local vicious = require("vicious")
datewidget = wibox.widget.textbox()
vicious.register(datewidget, vicious.widgets.date, "%b %d, %R")

最后添加到 wibox (statusbar) 中

    -- Add widgets to the wibox
    s.mywibox:setup {
        layout = wibox.layout.align.horizontal,
        { -- Left widgets
          ...
        },
        s.mytasklist, -- Middle widget
        { -- Right widgets
          ...
          datewidget
          ...
        },
xob 音量控制

Awesome 自己不提供音量控制插件,如果未经设置,还会发现原本键盘上的多媒体按键都会失效,这是因为没有让 Awesome 。下面实现一个音量控制插件,并且将音量控制绑定到按键上。

xob

install pulseaudio

sudo pacman -S pulseaudio pamixer

install xob

yay -S xob

Ready to use volume bar for pulseaudio

sudo pacman -S python-pip
pip install pulsectl
mkdir -p ~/.local/bin
vim ~/.local/bin/pulse-volume-watcher.py # copy from xob doc
chmod u+x ~/.local/bin/pulse-volume-watcher.py

为绑定音量热键,在 rc.lua 文件的 globalkeys 里加入

awful.key({ modkey }, "]",
    function ()
        awful.util.spawn_with_shell("pamixer -i 1")
    end, {description = "increase the volume", group = "custom"}),
awful.key({ modkey }, "[",
    function ()
        awful.util.spawn_with_shell("pamixer -d 1")
    end, {description = "decrease the volume", group = "custom"}),
awful.key({ modkey }, "\\",
    function ()
        awful.util.spawn_with_shell("pamixer -t")
    end, {description = "switch between mute and unmute", group = "custom"}),

开机启动 xob,配置下面自启动方案一后添加如下代码:

run_once({ "/home/kurome/.local/bin/pulse-volume-watcher.py | xob" })
屏幕亮度控制

用 xbacklight 命令

pacman -S xorg-xbacklight
xbacklight -set 50  # 亮度设置在 50%
xbacklight -inc 10  # 亮度增加 10%
xbacklight -dec 10  # 亮度减少 10%
xbacklight -get     # 获取当前亮度

消息交互

Naughty 是 Awesome 中的一个 lua 库,用于实现弹出消息框。利用 Naughty,我们可以实现在后台执行命令,然后在弹出消息框中显示结果。

首先要确保 rc.lua 的开头有这一行:

require("naughty")

如果想更改naughty的默认设置,可以添加下面的代码:

naughty.config.default_preset.timeout        = 5
naughty.config.default_preset.screen         = 1
naughty.config.default_preset.position       = "top_right"
naughty.config.default_preset.margin         = 4
naughty.config.default_preset.height         = 16
naughty.config.default_preset.width          = 300
naughty.config.default_preset.gap            = 1
naughty.config.default_preset.ontop          = true
naughty.config.default_preset.font           = beautiful.font or "Verdana 8"
naughty.config.default_preset.icon           = nil
naughty.config.default_preset.icon_size      = 16
naughty.config.default_preset.fg             = beautiful.fg_focus or '#ffffff'
naughty.config.default_preset.bg             = beautiful.bg_focus or '#535d6c'
naughty.config.presets.normal.border_color   = beautiful.border_focus or '#535d6c'
naughty.config.default_preset.border_width   = 1
naughty.config.default_preset.hover_timeout  = nil

下面利用sdcv实现一个词典工具,绑定到 Mod4 + d 上,更多的例子可以参考 官方的教程

awful.key({ modkey }, "d",
          function ()
             info = true
             awful.prompt.run({
                                 fg_cursor = "black",bg_cursor="orange", prompt = "<span color='#008DFA'>sdcv:</span>" }, 
                              mypromptbox[mouse.screen].widget,
                              function(word)
                                 local f = io.popen("sdcv -n " .. word)
                                 local fr = ""
                                 for line in f:lines() do
                                    fr = fr .. line .. 'n'
                                 end
                                 f:close()
                                 naughty.notify({ title = "<span color='red'>" .. word .. "</span>:", text = '<span font_desc="Sans 7">' .. fr ..'</span>', timeout = 5, width = 400, screen = mouse.screen })
                              end)
          end)
                               )

标题栏

搜索 titlebars_enabled ,设置为 false 来取消标题栏。

   { rule_any = {type = { "normal", "dialog" }
     }, properties = { titlebars_enabled = false }
   },

自启动

最简单的方式是在 rc.lua 里添加类似这样的代码:

awful.util.spawn_with_shell("firefox")
awful.util.spawn_with_shell("thunderbird")
awful.util.spawn_with_shell("amarok")
awful.util.spawn_with_shell("amule")

Rules of thumb when a shell(spawn_with_shell) is needed:

  • A shell is required when the commands contain &&, ;, ||, & or any other unix shell language syntax
  • When shell variables are defined as part of the command
  • When the command is a shell alias
方案一
local function run_once(cmd_arr)
    for _, cmd in ipairs(cmd_arr) do
      awful.spawn.with_shell(string.format("pgrep -u $USER -fx '%s' > /dev/null || (%s)", cmd, cmd))
    end
end

run_once({ "synergy" })
run_once({ "ibus-daemon -d -x -r -n awesome" })
run_once({ "compton --conf /home/kelu/.config/compton.conf" })
run_once({ "/home/kelu/Desktop/WeChat.desktop" })
方案二

To implement the XDG autostart specification, create autorun.sh and insert the following:

$ vim .config/awesome/autorun.sh
#!/usr/bin/env bash

function run {
  if ! pgrep -f "$1" ;
  then
    $@&
  fi
}

Then, make it executable.

chmod u+x .config/awesome/autorun.sh

要在自动启动中添加程序,只需在autorun.sh中添加run "program [some arguments]"run函数检查是否已经有一个参数相同的program实例,如果没有,则运行program

$ vim ~/.config/awesome/autorun.sh
...
run "fcitx5"

If everything is fine, add the following line to your rc.lua:

$ vim ~/.config/awesome/rc.lua
...
awful.spawn.with_shell("~/.config/awesome/autorun.sh")
...

自定义快捷键

查看键映射表

$ xmodmap -pm
shift       Shift_L (0x32),  Shift_R (0x3e)
lock        Caps_Lock (0x42)
control     Control_L (0x25),  Control_R (0x69)
mod1        Alt_L (0x40),  Meta_L (0xcd)
mod2        Num_Lock (0x94)
mod3      
mod4        Super_R (0x86),  Super_L (0xce),  Hyper_L (0xcf)
mod5        ISO_Level3_Shift (0x5c),  ISO_Level3_Shift (0x6c),  Mode_switch (0x85),  Mode_switch (0xcb)

编辑 rc.lua,在文件中查找文本

-- {{{ Key bindings
globalkeys = awful.util.table.join(

在此之下,可以添加您的自定义命令,例如:

-- {{{ Key bindings
globalkeys = awful.util.table.join(
     -- My Bindings
     awful.key({ }, "F1", function () awful.util.spawn_with_shell("terminator") end),

以下是我的一些例子,注意不要和原有的快捷键冲突噢:

    -- Custom
    awful.key({ "Control" ,           }, "3", function () awful.util.spawn_with_shell("shutter -f") end,
              {description = "screenshot", group = "custom"}),
    awful.key({ "Control" ,           }, "4", function () awful.util.spawn_with_shell("shutter -a") end,
              {description = "screenshot", group = "custom"}),
    awful.key({ "Control" ,           }, "5", function () awful.util.spawn_with_shell("shutter -s") end,
              {description = "screenshot", group = "custom"}),
    awful.key({ modkey,           }, "e", function () awful.util.spawn_with_shell("nautilus --no-desktop .") end,
              {description = "nautilus", group = "custom"}),
    awful.key({ modkey,           }, "w", function () awful.util.spawn_with_shell("/usr/share/typora/Typora /home/kelu/Workspace/document/todo.md") end,
              {description = "Typora", group = "custom"}),
--    awful.key({ modkey,           }, "w", function () awful.util.spawn_with_shell("nautilus --no-desktop .") end,
--              {description = "nautilus", group = "custom"}),
    awful.key({ modkey,           }, "b", function () awful.util.spawn_with_shell("google-chrome-stable") end,
              {description = "chrome", group = "custom"}),

我配置了自己的group叫custom,所有快捷键的信息可以使用 Mod4+S 快捷键查询。

关闭窗口快捷键

在方法 clientkeys = awful.util.table.join 内部添加:

    awful.key({ modkey,           }, "q",       function (c) c:kill()  end,
              {description = "close", group = "custom"}),
    awful.key({ altkey,           }, "q",       function (c) c:kill()  end,
              {description = "close", group = "custom"}),
关机
  • shutdown: systemctl poweroff
  • reboot: systemctl reboot
  • suspend: program pause &; systemctl suspend
  • logout: loginctl terminate-session ${XDG_SESSION_ID-}

其他

picom

compton,用于透明美化等功能的工具,如果你当前使用的软件支持窗口透明的功能,那么compton可以帮你设置透明度,阴影效果,窗口切换效果等:

sudo pacman -S picom 
mkdir -p ~/.config/picom/
cp -av /etc/xdg/picom.conf ~/.config/picom/picom.conf
  • Screentearing with NVIDIA’s proprietary drivers, Try this setting in picom.conf:

    vsync = true;
    
  • picom.sample.conf

启动与添加到自启动

$ picom &
$ vim ~/.config/awesome/rc.lua
run_once({ "picom" })

Start Numlock At Boot

You can start numlock at boot by installing this package:

sudo pacman -S numlockx

Then, insert this line in the ~/.xinitrc file two lines above the exec line:

numlockx &

Change Display Resolution And Refresh Rate

If you want to change the display resolution or the refresh rate of your monitor, you can use any of these programs.

NVIDIA cards:

sudo pacman -S nvidia-settings 

XFCE settings manager:

sudo pacman -S xfce4-settings

You can also use Xrandr to set your resolution and refresh rate. For a single 1080p monitor with 120Hz refresh rate, you can run this command

xrandr --output DP-0 --mode 1920x1080 --rate 120

You can also just use xrandr to see what displays are connected and what resolutions and refresh rates they support

xrandr

Awesome Menu

Awesome has a menu at the top left corner by default which may be useless to some people. You can disable it if you want by commenting the lines like mentioned below. These lines should start from 83.

-- {{{ Menu
-- Create a launcher widget and a main menu
--myawesomemenu = {
--   { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end },
--   { "manual", terminal .. " -e man awesome" },
--   { "edit config", editor_cmd .. " " .. awesome.conffile },
--   { "restart", awesome.restart },
--   { "quit", function() awesome.quit() end },
--}--mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
                                      { "open terminal", terminal }
--                                    }
--                         }}--mylauncher - awful.widget.launcher({ image = beautiful.awesome_icon,
--                                     menu = mymainmenu })

Force An Application To A Specific Tag

You can force an application to always open in a specific tag. To do this, add these lines in the rc.lua file

-- Set applications to always map on the tag 7 on screen 1.
{ rule = { class = "Thunderbird" },
    properties = { screen = 1, tag = awful.util.tagnames[7] , switchtotag = true  } },

Here, we want Thunderbird to open on tag 7 and on screen 1. You can modify and duplicate to as many applications you want

输入法

安装

pacman -S ibus ibus-rime

在 Awesome 的 rc.lua 中加入以下语句,让 Awesome 启动的时候顺便运行 ibus-daemon

awful.util.spawn("ibus-daemon -d -x -r -n awesome")
  • -d: run ibus as background process.
  • -x: execute ibus XIM server.
  • -r: if there is an old ibus-daemon is running, it will be replaced.
  • -n: specify the name of desktop session. [default=gnome]

另外为了让各种图形库都能识别 ibus ,还需要在 $HOME/.profile 中加入这几个 环境变量:

export GTK_IM_MODULE=ibus
export XMODIFIERS=@im=ibus
export QT_IM_MODULE=ibus

屏保和休眠管理

这个其实好办, Xorg 下的e屏保有很多选择,而且大都附带休眠管理的配置。我挑了 “老牌”的 xscreensaver

pacman -S xscreensaver

在 Awesome 的 rc.lua 配置文件里加入:

awful.util.spawn("xscreensaver -no-splash")

xscreensaver 的配置选项也很多,不过我们可以通过 xscreensaver-demo 命令用图 形界面进行设置,所有选项都保存在 $HOME/.xscreensaver 文件中。

添加一个快捷键:

awful.key({ "Mod4" }, "l",
    function ()
        awful.util.spawn_with_shell("xscreensaver-command --lock")
    end, {description = "lock the screen immediately", group = "custom"}),

rofi

安装

pacman -S  rofi
mkdir -p ~/.config/rofi/
rofi -dump-config > ~/.config/rofi/config.rasi

添加快捷键(base on awesome-copycats)

    awful.key({ modkey }, "r", function ()
            os.execute(string.format("rofi -show %s -theme %s",
            'drun', ''))
        end,
        {description = "show rofi", group = "launcher"}),
    awful.key({ altkey }, "r", function ()
            os.execute(string.format("rofi -show %s -theme %s",
            'run', ''))
        end,
        {description = "show rofi", group = "launcher"}),
    awful.key({ altkey }, "Tab", function ()
            os.execute(string.format("rofi -show %s -theme %s",
            'window', ''))
        end,
        {description = "show rofi", group = "launcher"}),

mpd&ncmpcpp

介绍

mpd:mpd是一个播放本地音乐的后台服务

ncmpcpp:ncmpcpp是mpd的TUI前端,用来控制mpd的工作,此外也提供一些方便的功能比如查找歌曲,支持flac等多种音乐格式

总之两者是client/server的关系

安装即可:

pacman -S mpd ncmpcpp

配置方法

配置分为两步,一步是mpd的,另一步是ncmpcpp的

这里建议直接用我的配置。记得把~/.config/mpd/mpd.conf~/.config/ncmpcpp/config里面的音乐目录改成你的音乐路径即可

我的配置声音输出用的是pulseaudio,所以你需要保证你的pulseaudio工作正常。

使用

启动mpd服务

systemctl start mpd.service --user

之后启动ncmpcpp,具体用法这里不再赘述,直接查看archwiki或者在ncmpcpp里面按F1即可看到。

出现问题之后首先检查mpd的日志

systemctl status mpd --user

根据不同的提示来解决问题

如果没有声音,首先检查一下pulseaudio,用pulseaudio的GUI配置工具pavucontrol即可

flameshot

awful.key({ modkey, "Shift" }, "Print",
    function ()
        awful.util.spawn_with_shell("flameshot gui")
    end, {description = "Switch between windows", group = "custom"}),

awesome-copycats

git clone --recurse-submodules --remote-submodules --depth 1 -j 2 https://github.com/lcpz/awesome-copycats.git
mv -bv awesome-copycats/{*,.[^.]*} ~/.config/awesome; rm -rf awesome-copycats
mv -bv awesome-copycats/* ~/.config/awesome; rm -rf awesome-copycats

Additional default software used:

pacman -S curl alsa-utils dmenu firefox mpc mpd flameshot unclutter xorg-xbacklight xsel slock

然后再 rc.lua 取消掉一些注释以启用某些功能

  • rofi instead of dmenu

  • flameshot shotcut

  • slock: To unlock slock, simply type your user password at the blank screen. You can verify this is the process via slock’s readme.

        awful.key({ modkey }, "l", function () os.execute("slock") end,
            {description = "lock screen", group = "hotkey"}),
    
  • Unclutter hides your X mouse cursor when you do not need it, to prevent it from getting in the way.

  • xsel — Command-line program for getting and setting the contents of the X selection.

  • mpd & mpc: Music Player Daemon

Touchpad

disable touchpad:

$ sudo pacman -S  xorg-xinput
$ xinput list | grep Touchpad
⎜   ↳ FTE1200:00 0B05:0501 Touchpad            id=15 [slave  pointer  (2)]
$ xinput disable "FTE1200:00 0B05:0501 Touchpad"
$ vim .xinitrc
xinput disable "FTE1200:00 0B05:0501 Touchpad"

xbindkeys

sudo pacman -S pulseaudio xbindkeys

之后通过 xbindkeys --multikey 获取键盘上与音量有关的 keycodes,也就是 f10、f11、f12

$ xbindkeys --multikey
"(Scheme function)"
    m:0x0 + c:76
    F10
"(Scheme function)"
    m:0x0 + c:95
    F11
"(Scheme function)"
    m:0x0 + c:96
    F12

通过如下命令添加默认配置

xbindkeys -d > ~/.xbbaindkeysrc

将如下代码添加到 ~/.xbindkeysrc 最后

# Increase volume
"pactl set-sink-volume @DEFAULT_SINK@ +1000"
    m:0x0 + c:96
    F12

# Decrease volume
"pactl set-sink-volume @DEFAULT_SINK@ -1000"
    m:0x0 + c:95
    F11

# Mute volume
"pactl set-sink-mute @DEFAULT_SINK@ toggle"
    m:0x0 + c:76
    F10

然后启用:

xbindkeys

redshift

$ sudo pacman -S redshift
$ cat >> ~/.config/awesome/rc.lua
awful.util.spawn("redshift-gtk -l 39.5:116.2")

Tip: You can get the coordinates of a place with GeoNames.org

autostart 效果不是很好,所以改为手动启用