Arch Linux 星球

September 23, 2022

中文社区新闻

从软件仓库中移除了 python2

Python 2 已于 2020 年 1 月结束了生命。我们一直在积极地减少软件仓库中对 python2 依赖的项目数量,终于到了最后能从我们的发行版中移除掉它的时候了。如果你的系统中仍然安装着 python2 请考虑删除掉它和所有 python2 的包。
如果你还需要使用 python2 包当然可以还留着,但是请注意它不再会收到安全更新。如果你需要打补丁的版本,请考虑使用 AUR 或者 非官方的第三方仓库

by farseerfc at September 23, 2022 02:17 PM

September 17, 2022

中文社区新闻

Arch Linux 邮件列表的变化

作为抛弃已经EOL的Python2工作的一部分,我们从 mailman2 迁移到了 mailman3 。

为了保持 DKIM 签名完整,我们已经关掉了改写 “From” 邮件头和主题(附加上列表名)的功能。这意味着想要回复邮件列表的时候,必须使用“回复邮件列表”功能,以及你可能需要更新你的过滤规则以匹配新的”From” 邮件头。

过去订阅过邮件列表的邮箱已经迁移到新的列表,不需要重新订阅。不过要管理你的订阅设置,必须重新注册新的 mailman3 账户。

by farseerfc at September 17, 2022 12:57 AM

September 10, 2022

frantic1048

GSC - Nendoroid 凯露

Karyl

最近终于稍微忙过来了,拍一波到了许久的凯露。本文不会有可怕的接头环节。

首先是盒子和藏在神奇位置的作为特技的装饰物。部件非常多,甚至连尾巴都是可拆卸的。

Karyl Karyl

接下来是本体! Karyl

素材力很高的书。 Karyl

「疯狂星期四,V 我 50」 Karyl

「快给我,不然……」 Karyl

「这就对了」 Karyl

「(ಥ︿ಥ)」 Karyl

September 10, 2022 05:28 PM

September 07, 2022

百合仙子

让离线软件真正离线

本文来自依云's Blog,转载请注明。

去年我做了个索引 Telegram 群组的软件——落絮,终于可以搜索到群里的中文消息了。然而后来发现,好多消息群友都是通过截图发送的,落絮就索引不到了。也不能不让人截图嘛,毕竟很多人描述能力有限,甚至让复制粘贴都能粘出错,截图就相对客观真实可靠多了。

所以落絮想要 OCR。我知道百度有 OCR 服务,但是我显然不会在落絮上使用。我平常使用的 OCR 工具是 tesseract,不少开源软件也用的它。它对英文的识别能力还可以,尤其是可自定义字符集所以识别 IP 地址的效果非常好,但是对中文的识别能力不怎么样,图片稍有不清晰(比如被 Telegram JPEG 压缩)、变形(比如拍照),它就乱得一塌糊涂,就不说它给汉字之间加空格是啥奇怪行为了。

后来听群友说 PaddleOCR 的中文识别效果非常好。我实际测试了一下,确实相当不错,而且完全离线工作还开源。但是,开源是开源了,我又没能力审查它所有的代码,用户量太小也不能指望「有足够多的眼睛」。作为基于机器学习的软件,它也继承了该领域十分复杂难解的构建过程,甚至依赖了个叫「opencv-contrib-python」的自带了 ffmpeg、Qt5、OpenSSL、XCB 各种库的、不知道干什么的组件,试图编译某个旧版 numpy 结果由于太旧不支持 Python 3.10 而失败。所以我决定在 Debian chroot 里安装,那边有 Python 3.9 可以直接使用预编译包。所以问题来了:这么一大堆来源不明的二进制库,用起来真的安全吗?

我不知道。但是我知道,如果它联不上网的话,那还是相对安全的。毕竟我最关心的就是隐私安全——一定不能把群友发的图片泄漏给未知的第三方。而且联不上网的话,不管你是要 DDoS 别人、还是想挖矿,收不到指令、传不出数据,都行不通了嘛。我只要它能从外界读取图片,然后把识别的结果返回给我就好了。

于是一个简单的办法是,拿 bwrap 给它个只能访问自己的独立网络空间它不就访问不了互联网了吗?不过说起来简单,做起来还真不容易。首先,debootstrap 需要使用 root 执行,执行完之后再 chown。为了进一步限制权限,我使用了 subuid,但这也使得事情复杂了起来——我自己都难以访问到它了。几经摸索,我找到了让我进入这个 chroot 环境的方法:

#!/bin/bash -e

user="$(id -un)"
group="$(id -gn)"

# Create a new user namespace in the background with a dummy process just to
# keep it alive.
unshare -U sh -c "sleep 30" &
child_pid=$!

# Set {uid,gid}_map in new user namespace to max allowed range.
# Need to have appropriate entries for user in /etc/subuid and /etc/subgid.
# shellcheck disable=SC2046
newuidmap $child_pid 0 $(grep "^${user}:" /etc/subuid | cut -d : -f 2- | tr : ' ')
# shellcheck disable=SC2046
newgidmap $child_pid 0 $(grep "^${group}:" /etc/subgid | cut -d : -f 2- | tr : ' ')

# Tell Bubblewrap to use our user namespace through fd 5.
5< /proc/$child_pid/ns/user bwrap \
  --userns 5 \
  --cap-add ALL \
  --uid 0 \
  --gid 0 \
  --unshare-ipc --unshare-pid --unshare-uts --unshare-cgroup --share-net \
  --die-with-parent --bind ~/rootfs-debian / --tmpfs /sys --tmpfs /tmp --tmpfs /run --proc /proc --dev /dev \
  -- \
  /bin/bash -l

这里给了联网权限,是因为我需要安装 PaddleOCR。没有在创建好 chroot 之后、chown 之前安装,是因为我觉得拿着虽然在 chroot 里但依旧真实的 root 权限装不信任的软件实在是风险太大了。装好之后,再随便找个图,每种语言都识别一遍,让它下载好各种语言的模型,接下来它就再也上不了网啦(为避免恶意代码储存数据在有网的时候再发送):

#!/bin/bash -e

dir="$(dirname $2)"
file="$(basename $2)"

user="$(id -un)"
group="$(id -gn)"

# Create a new user namespace in the background with a dummy process just to
# keep it alive.
unshare -U sh -c "sleep 30" &
child_pid=$!

# Set {uid,gid}_map in new user namespace to max allowed range.
# Need to have appropriate entries for user in /etc/subuid and /etc/subgid.
# shellcheck disable=SC2046
newuidmap $child_pid 0 $(grep "^${user}:" /etc/subuid | cut -d : -f 2- | tr : ' ')
# shellcheck disable=SC2046
newgidmap $child_pid 0 $(grep "^${group}:" /etc/subgid | cut -d : -f 2- | tr : ' ')

# Tell Bubblewrap to use our user namespace through fd 5.
5< /proc/$child_pid/ns/user bwrap \
  --userns 5 \
  --uid 1000 \
  --gid 1000 \
  --unshare-ipc --unshare-pid --unshare-uts --unshare-cgroup --unshare-net \
  --die-with-parent --bind ~/rootfs-debian / --tmpfs /sys --tmpfs /tmp --tmpfs /run --proc /proc --dev /dev \
  --ro-bind "$dir" /workspace --chdir /workspace \
  --setenv PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
  --setenv HOME /home/worker \
  -- \
  /home/worker/paddleocr/ocr.py "$1" "$file"

kill $child_pid

这个脚本会把指定文件所在的目录挂载到 chroot 内部,然后对着这个文件调用 PaddleOCR 来识别并通过返回结果。这个调用 PaddleOCR 的 ocr.py 脚本位于我的 paddleocr-web 项目

不过这也太复杂了。后来我又使用 systemd 做了个服务,简单多了:

[Unit]
Description=PaddleOCR HTTP service

[Service]
Type=exec
RootDirectory=/var/lib/machines/lxc-debian/
ExecStart=/home/lilydjwg/PaddleOCR/paddleocr-http --loglevel=warn -j 2
Restart=on-failure
RestartSec=5s

User=1000
NoNewPrivileges=true
PrivateTmp=true
CapabilityBoundingSet=
IPAddressAllow=localhost
IPAddressDeny=any
SocketBindAllow=tcp:端口号
SocketBindDeny=any
SystemCallArchitectures=native
SystemCallFilter=~connect

[Install]
WantedBy=multi-user.target

这里的「paddleocr-http」脚本就是 paddleocr-web 里那个「server.py」。

但它的防护力也差了一些。首先这里只限制了它只能访问本地网络,TCP 方面只允许它绑定指定的端口、不允许调用 connect 系统调用,但是它依旧能向本地发送 UDP 包。其次运行这个进程的用户就是我自己的用户,虽然被 chroot 到了容器里应该出不来。嗯,我大概应该给它换个用户,比如 uid 1500,应该能起到跟 subuid 差不多的效果。

顺便提一句,这个 PaddleOCR 说的是支持那么多种语言,但实际上只有简体中文等少数语言支持得好(繁体都不怎么样),别的语言甚至连语言名和缩写都弄错,越南语识别出来附加符号几乎全军覆没。

by 依云 at September 07, 2022 07:12 AM

frantic1048

在 LilyPond 乐谱中插入有动态的 Ghost Note

一开始用 LilyPond 打谱的时候,学到的插入 ghost note 的方式是使用 \parenthesize 来给音符弄个括号,这样生成的乐谱阅读上没有什么问题,但是生成的音频听起来的话,音符加 \parenthesize 前后声音完全没有区别,试听乐谱的时候不太方便。搜了一圈 2019 年也有人遇到同样问题,但是看起来没有后续。

目标上来说希望有个神奇命令能搓出一个「真」的 ghost note:让单个音符生成的乐谱和 \parenthesize 效果一样,同时生成的音频里音量能比普通音符小一些。

先来波折腾结果示例,两个小节里的 ghost note(那些带括号的音符)分别由 LilyPond 的 \parenthesize 和本文介绍的自定义函数生成。

ghost-note-comparison

ghost-note.ly

过程

文档翻啊翻,找到 midi-extra-velocity 这个影响音符音量,但是又不会像强弱记号那样影响后续乐谱音量的属性,加上现成的 \parenthesize 加个括号,搓一个函数:

ghosta = #(define-scheme-function
           (note)
           (ly:music?)
           (displayMusic note)
           (ly:music-set-property! note 'midi-extra-velocity -50) ;; 减小音量
           (displayMusic note)
           #{
             < \parenthesize #note >
           #}
           )
 
% 乐谱里用 `\ghosta sn4` 这样的方式调用

define-scheme-function 用法参见 2.2.1 Scheme function definitions。第三个参数开始的所有内容会被依次求值,最后一个部分会作为函数的返回值。

displayMusic 是 LilyPond 提供的检查音符(音乐)内容的函数,它能把音符在 LilyPond 内部的表示形式给显示出来(默认显示在 lilypond 的命令行输出里),一般会看到的是一串更低级的 LilyPond 函数的调用,调试 Scheme 函数的时候很有用。

对于刚刚实现的那个函数,写一个 \ghosta sn4,函数里两个 displayMusic 会给出下面的信息(为了方便阅读,下面内容有手动排版和加注释,不影响含义):

;; 修改之前的音符
(make-music 'NoteEvent
  'drum-type 'snare
  'duration (ly:make-duration 2))
 
;; 修改之后的音符
(make-music 'NoteEvent
  'midi-extra-velocity -50
  'drum-type 'snare
  'duration (ly:make-duration 2))

可以看到预期的属性被设置上去了,接下来检查乐谱,括号显示没啥问题,然后是音频,发现音符的音量并没有什么变化,这不太对劲。

接着翻着翻到了源代码,发现 midi-extra-velocity 看起来只有在音符的 articulations 属性里面的元素上才有机会被用上LilyPond - Internal Reference 对 articulations 属性的描述是

articulations (list of music objects)

Articulation events specifically for this note.

直接在音符的属性上修改属性看起来不太行了,那就照它说的搓一个设定了midi-extra-velocity 属性的 ArticulationEvent。用来生成 ArticulationEvent 的内置函数 make-articulation 需要一个额外的 name 参数为了避免更多的奇奇怪怪错误, 尝试用了 LilyPond 已有的 accent 这个用于重音的 articulation 名。

ghostb = #(define-scheme-function
           (note)
           (ly:music?)
           #{
             < \parenthesize #note #(make-articulation "accent" 'midi-extra-velocity -50) >
           #}
           )
 
% 乐谱里用 `\ghostb sn4` 这样的方式调用

这下声音调整成功了,但是音符多了个重音记号,这个还好,能用 \omit 去掉。

TL;DR

于是生成 ghost note 的能用的函数终于搓出来了:

ghost = #(define-scheme-function
           (note)
           (ly:music?)
           #{
             <\parenthesize #note \omit #(make-articulation "accent" 'midi-extra-velocity -50)>
           #}
           )
 
% 乐谱里用 `\ghost sn4` 这样的方式调用

参考

September 07, 2022 12:00 AM

September 01, 2022

Lainme

在Centos7/8上使用HillstoneVPN

公司内网使用的是Hillstone的SSL VPN,官方的Linux客户端只支持非常有限的几个老发行版,连Centos7都不支持。不过有些发行版并非真的不能用,只是程序里对发行版做了判断,不是指定的几个就强行退出。

后来我发现有人做了一个移除了版本限制的客户端,在Centos7下面可以完美运行:https://github.com/JackMoriarty/HillstoneVPN/releases

但在Centos8上运行时总是报错,

Unit dbus-org.freedesktop.resolve1.service not found

这个报错的服务是systemd-resolved的,默认没有开启。但我启动systemd-resolved服务后,虽然不再报错,VPN连接后却完全无法上网。搜索后才知道systemd-resolved更改了很多原本的机制,VPN软件必须做相应的修改才能使用。那么怎么才能不用systemd-resolved,又不让客户端报错呢?我花了几个小时终于发现了最简单的解决方法,删除/sbin/resolvconf

虽然客户端可以用了,但我对这个客户端很不满意:

  • 无法做成systemd服务开机自动启动和连接指定的VPN
  • 需要在桌面持续运行,无法很好的最小化

经过一些搜索后,我发现了一个第三方的命令行客户端Hilldust (https://github.com/LionNatsu/hilldust),基本能解决上述问题,但它也有自己的一点不足

  • 不支持从配置文件读入参数,在多人使用的电脑上会暴露密码
  • 没有从VPN服务器返回的路由表中设置路由,对我们公司的网络无法设置正确的路由
  • 不能很好的作为systemd服务运行,主要是退出时的判断仅有KeyInterrupt,没有考虑SIGNAL。
  • 使用iproute2来配置网络,可能和NetworkManager冲突。另外网络复位主要靠save & restore,如果在VPN运行期间还做了其他网络的调整,复位的时候也会回退这些调整。

于是我在Hilldust上重新做了一个Wrapper,处理了一下这些地方,在Centos7/8上都测试通过了。如果你也需要的话,可以试试:https://github.com/lainme/hilldustWrapper

by lainme (lainme@undisclosed.example.com) at September 01, 2022 01:06 PM

August 30, 2022

中文社区新闻

Grub引导器升级的配置文件不兼容

grub 包最近的变化给 fwsetup 命令增加了新的命令参数,改变了生成出的引导配置中调用命令的方式。根据你的硬件和设置这可能导致系统不能引导,因为安装的引导器和配置文件之间不兼容。在 grub 包升级之后,我们建议重新运行以下命令,安装和再次生成配置:

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

关于 grub-install 的具体用法请参考 wiki 页面:GRUB – ArchWiki

by farseerfc at August 30, 2022 11:36 PM

August 22, 2022

frantic1048

给罗技 G903 换微动

用了三年多的 G903 最近按键开始双击了,网上翻了一下有现成的微动板可以直接换,于是就搞了一组同款欧姆龙 D2F-F-K(50M) 微动板,以及一组新的脚贴(因为会被拆掉)来拯救鼠标。工具上需要 1.5 的十字螺丝刀、T5 螺丝刀、镊子和翘棒,以及肯定会发现鼠标超级脏大量积灰皮屑谜之毛茸茸,弄点清洁工具。图里左右侧蓝色线的那个东西就是微动板。

G903

首先顶盖有 1 颗露出来的 T5 螺丝和 6 颗隐藏的十字螺丝。开关上方(靠近 USB 口)这颗是 T5 螺丝,直接拧下。

G903

下面这几颗,需要先拆掉图里圈出来的位置的胶贴,以及撬开一点点开关下方的贴纸,用镊子去戳边缘,然后撬开就行。

G903 G903 G903

然后可以捏着鼠标侧面来分开顶盖和底板,然后从 USB 口那侧掀开,绝对不能大力,因为两者还有排线连着。

G903

上图方框的位置是排线的插口,那个白色的东西可以掀开,然后排线就会松开;圆圈是电池的线,那个就直接拔下来,比较费劲,需要小心不要把插座弄坏了。

G903 G903 G903

连接的部分弄开之后就可以拿开底板了,接下来是电池(它挡住了一颗需要拆的螺丝 (┙>∧<)┙ へ ┻┻),电池是背面被双面胶粘着的,用翘棒温柔地从电池一侧捅进去,一点点分离开电池底面有胶的地方,过程电池会稍微一点点变形,问题不大 ˊ_>ˋ 。

G903

电池掀掉之后是下面这几个螺丝,拆了之后,把鼠标侧键的盖子扒下来,分离手掌部位的那块外壳。

G903

然后是鼠标左右键的螺丝。

G903

这个时候已经能看到微动了,但是还得继续拆中间滚轮上面那个盖子的螺丝。

G903

然后拆固定微动板子的螺丝。

G903

然后拔掉图里的插头,分离旧的微动板,把新的微动板的插头插到相同的位置,这样换微动就算完成了。

G903

最后,看到一堆零件后悔也没用了,怎么拆的就怎么组装回去,鼠标就好了。这鼠标真是难拆!!!

G903

August 22, 2022 07:00 PM

August 18, 2022

Alynx Zhou

不应该做 EVA,而应该做环太平洋

想必把 2018 年的 DARLING in the FRANXX (名字太长了,后面简称 DitF 吧)称作有争议的作品应该不会有人反对,不过我恰好是个不喜欢追新番的人,不然也许我在 2018 年写篇关于这个的博客应该会能获得不少点击量。总之我在 2022 年下载了全集并且几乎是不间断的在三天之内看完了,可能不是特别好评价,但是觉得还是得写点什么。如果读者觉得“怎么复读了很多已有的观点”或者“和我想看的完全不一样”,还麻烦多包涵或者自行关闭标签页。

我认为这是一个优点和缺点同样突出的作品,倒不像很多人觉得是烂尾,结尾至少情理之中可以接受。主要问题是在于塑造人物形象和完善背景设定之间的冲突,也就是标题里写的“不应该做 EVA,而应该做环太平洋”。看完之后我半开玩笑地和 @垚 说:“都怪庵野秀明,非要在巨大机器人动画里面加上一堆反乌托邦末世玄学宗教的背景设定,导致后来的巨大机器人动画不这么做就好像缺了点什么一样。”我其实对巨大机器人动画不算是专家(比如我显然没看过高达),但我觉得对 EVA 还算是熟悉。

虽然我想说的问题是剧情方面的,不过还是要简单提一下作画。@垚 和我表示他一开始是奔着 TRIGGER 才去看的 DitF,结果看过之后对于动作场面大失所望。我其实也不是特别了解 TRIGGER,只是之前被他拉去电影院看了普罗米亚。一定要比较的话确实不管是美术风格还是动作场面都没有普罗米亚那么有特色,但我还是觉得至少在及格线以上了。我觉得特别出色的是机体的设计,不管是 EVA 还是环太平洋,机体设计都是偏向机械化的(虽然 EVA 内在是生物,但是外表仍然是机械),同时是男性化的设计。我还是头一次在动漫里看到女性形象和不是特别机械化设计的机体,而且甚至有丰富的表情,非常新鲜的同时也很符合设定(实际上动画里很多时候使用了 FRANXX 的形象代替女性寄驶员),属于是一个巨大的加分项。叫龙的设计也算新鲜,至少对我来说,看第一集的时候我明明期待的是出来一个传统的怪兽形象的,结果出来的是这么一种可以算是放飞自我的东西。虽然在逻辑上可能比较难以解释它的存在性,不过好在是动漫嘛,不需要在里面找现实。(话说回来了解我的朋友应该知道我最喜欢的敌人设计是 NieR: Automata 里面的机械生命体。)

然后说到我最关注的剧情了,我始终认为作品的核心是剧情。而且剧情是很难把控的东西,特别是对于原创剧情的作品来说,把剧情写好真的是一种不多见的能力。在我看来 DitF 在人物形象和感情戏上达到了一个极高的高度。主要人物有很多,但是每个人的个性都很清晰,并且我没有觉得哪个人的性格令我讨厌。同时故事本身不是简单直白一眼看到头的类型。比如第 13 集将故事推向了高潮,不仅仅因为这一集本身讲述的内容非常感人,而且将整个剧情前半部分埋下的伏笔全部都衔接上了(我甚至差点以为剧情要按绘本发展走向 bad end)。贯穿全篇出现过不止一次的“比翼鸟”的比喻,也非常的符合主题。但我在看一些二创视频的时候还是能看到一些 2018 年的评论在说第 14 集的剧情是喂屎,我确实可以理解追更的朋友当初等了一周之后看到这些阴差阳错然后还要提心吊胆等上一周才能看到下一集的焦躁心情,但这一集的矛盾激化成功的在一个高潮之后推进了剧情的节奏同时与下一集的高潮形成对比,而且这一集的内容非常的合理,虽然是各种巧合,但又很符合现实,符合人物的心理和动机。至于其它一些风评不好的部分比如搭档交换的剧情,只能说是见仁见智,有人不喜欢无可厚非,我还是觉得这部分也增加了故事的复杂性。

但是与塑造人物形象形成对比,完善背景设定方面我认为有比较严重的硬伤。24 集里面在前半部分简单介绍了一个可以说是反乌托邦的设定,然后大量的篇幅用来刻画人物之间的关系和人物细腻的心理活动,结果在感情戏达到高潮之后仿佛是编剧突然想起来“啊,我们挖了好大的坑还没填呢”一样,开始匆忙的填之前的坑。比如我到现在也没想通只在第 15 集里出现了一次的那个巨大的手到底是什么和有什么存在意义。比如叫龙公主在第 17 集开始有大篇幅的剧情之前几乎没有任何铺垫(你别告诉我第 15 集核心里掉出来的小人就算铺垫了),这和感情戏部分各种伏笔先放好然后再衔接完全不像是一部作品的风格,反正你让我看到前半段各种叫龙出现之后是想不到有这么一个个体的存在的。第 19 集通过介绍博士的角度介绍了人类向不死方向的发展,多少算是成功地填了一部分坑。然后整个作品似乎就陷入了“编剧发现还剩五集了填不完坑了于是开始放飞自我”的方向发展了,星实体是个什么东西?之前完全没铺垫过,现在强行在一集内塞给观众。鹤望兰·天燕座又是怎么来的?就算不在动漫里找现实,这也过于不符合逻辑了,看看别的叫龙是什么样子,它们怎么搞出这么个造型的啊。到底是博士发癫了还是编剧发癫了?然后可能是由于实在没办法了,机械降神一个最大反派叫做 VIRM,这已经不是硬伤了,这是直接一刀把脑袋砍掉从脖子往下截肢了。于是一个完全没有铺垫,思维及其简单,做事不讲逻辑的工具 boss 出现了……你可以告诉我打了半年的敌人并不是真正的敌人,但我不能容忍你用这么一种侮辱观众智商的方式告诉我这个事实。然后再一次放飞自我把科技水平拉到太空时代,说实在的,这个和反派登场比起来,已经到了我看见什么都不惊讶的程度了。然后再经过几集漫长的毫无必要的人类叫龙和 VIRM 的太空混战剧情终于结束了这种煎熬,我只能说这部分比起种田是完全的不讲道理了。

至于最后大结局的“生孩子”剧情,想必也有很多人不满意,虽然我自己讨厌小孩,但我觉得这部分不是什么问题。可能是考虑到当下的现实环境,年轻人确实对催生比较反感吧,但放在剧情里面,作为一个新世界开始的必要环节实际上是没什么问题的。最后黑色头发的少年和樱花色头发的少女在树下相遇的结尾也是我最满意的部分之一了,或者可能我恰好是一个容易被这种剧情打动的人吧。

另外 @垚 表示同样是 TRIGGER 的作品,普罗米亚的剧情要好很多,但我不是特别同意,我个人觉得普罗米亚最后 1/4 的剧情其实也向着强行收尾的方向走了,不过一部剧场版动画和季番还是不一样的,剧场版动画只有两个小时,不会有太复杂的背景设定,故事本身又是快节奏,就算强行推进一下剧情,观感也不会太差,而且独特的美术风格和配乐相当大程度的掩盖了剧情的问题。

总而言之,我其实是不太在意“符合逻辑但是大部分观众都不喜欢”的剧情的,比起这个,我觉得“不符合逻辑”的剧情问题要严重得多,这说明剧情走向已经变成无法把控的东西了,为了强行在剩下的集数里面结束故事,不得不强行引入一些东西。如果你问我怎么修改剧情能解决掉硬伤,我其实一开始也没什么思路。不过和 @垚 简单聊了一下之后我意识到了问题所在。从感情戏的篇幅和水平来看,很显然巨大机器人战斗只是个载体,这应该是个披着机器人战斗的皮的爱情故事,并且爱情故事部分相当的成功。在我看来 DitF 的思维和 EVA 其实是有相当大的差别的,虽然可能大家总是津津乐道碇真嗣、绫波零、明日香、渚薰之间的情感关系,但 EVA 没有对这部分的直接描写,更多是通过侧面细节描写,以及粉丝进行的分析推理得出的。所以 EVA 可以在背景设定上挖很大的坑然后有充足的时间填坑。因此我的想法是让这个作品向环太平洋的方向靠近,去掉反乌托邦的设定,比如什么种植园和 APE 都可以不要,直接快进到不知道为什么出现了名叫叫龙的怪兽进攻人类,于是人类设计了 FRANXX 并要少年少女操作来防御。然后人物的背景全部都不需要修改,最终的结局就像环太平洋一样直接摧毁掉叫龙来源就可以了。虽然可能会被 EVA 观众认为“没有达到 EVA 的高度”,但既然本来就不在一个赛道上(我这写的是爱情故事啊),这也没什么所谓了。考虑到第 13 集和 15 集的口碑,这样改应该不会折损它的优点。

最后还是要说,虽然有这么明显的硬伤,这部作品突出的优点还是让我受到了很大震撼并且在接下来的一周都沉浸在剧情里不能自拔。我个人也非常喜欢这部作品的 ED,无论是旋律还是歌词,以及演唱方面都可以说是一流的作品,特别是第 13 集高潮部分的《ひとり》,单从音乐和剧情结合的角度来说,确实达到了 EVA 的高度(让我想起来《翼をください》。《トリカゴ》也是绝妙的作品,特别是伴随 ED 出现的画面,“如果这些人物所在的是一个没有叫龙和 FRANXX 存在的世界会是什么样子呢?”(很遗憾我对 OP 没什么感觉)我同样对广、02、五郎和莓的人物形象非常的喜爱,复杂的情感关系使得这些人物变得颇为立体,而且他们完全没有动漫里一些经常出现的会让我讨厌的人物特质。

by Alynx Zhou (alynx.zhou@gmail.com) at August 18, 2022 05:06 PM

百合仙子

我所讨厌的网页行为

本文来自依云's Blog,转载请注明。

有些网页的行为通常不被视为 bug,甚至是故意为之,但很令人讨厌。这里记录一些我所讨厌的网页「特性」。它们被归为两类,要么导致某些场景下用不了,或者用着很不方便,要么很打扰人。

可访问性问题

忽视系统、浏览器设置,在浏览器使用浅色主题的情况下默认使用深色主题,或者在浅色主题下代码部分使用深色主题。反过来问题不大,因为我有 DarkReader

主体文本不支持选择和复制。选择和复制之后,用户能做很多事情,比如查生字、翻译、搜索相关主题。

已访问链接与未访问链接显示没有差别。

消除可交互元素(链接、按钮)的 outline。这个 outline 以前是虚线框,现在火狐改成了蓝色框,用于标识当前键盘交互的对象。

搜索框不支持回车确认,必须换鼠标点击。

位于文本框后的按钮不支持使用 Tab 键切换过去,并且 Tab 键在此文本框中也没有任何显著的作用。必须换鼠标点击。

需要交互的元素不能被 vimium 插件识别为可点击。这大概是使用非交互元素来处理交互事件,甚至事件监听器都不在元素本身上。

使用 JavaScript 实现原本可以直接用链接实现的内容(链接目标是某个 JavaScript 函数调用)。这导致我无法使用中键来在新标签页中打开。

显著不同的内容没有独立的 URL。尤其见于一些单页应用(SPA)。要到达特定内容(比如加书签或者分享给别人),就只能记录先点哪里、再点哪里等。

预设用户的屏幕大小,导致浏览器窗口过小的时候部分关键内容(如登录按钮)看不到、无法操作。

交互元素没有无障碍标签。成堆的「未加标签 按钮」。

通过 User-Agent 判断浏览器,并拒绝为某些 User-Agent 服务(但实际上去除这个限制之后,功能是完全没有问题的)。

当没有带声音自动播放权限时,无声播放主体内容(而非等待用户操作使其获得权限)。说的就是 Bilibili。

为大屏幕用户(如桌面用户)展示为手机屏幕设计的页面。这些页面中字体特别巨大,并且不能被浏览器缩放影响。交互元素上鼠标指针不改变为手状,甚至只支持触摸操作而不支持鼠标点击。

悬浮于主内容之上的「在App中打开」。点名批评 imgur。它的按钮不光挡住图片,而且用户放大图片的时候它也被放大,挡住更多图片内容。

不能禁用的图片懒加载,或者视频内容被移出画面、切换到后台就停止加载。点名批评 Telegram、维基百科。我等你加载呢,你非要我盯着看你加载浪费时间?现在网好,你赶紧给我加载好,进电梯或者地铁或者山洞了,我再慢慢看你的内容啊。

视频内容被移出画面就停止播放。点名批评知乎。我让你播放你就给我播放。我不看视频,是因为视频画面没啥可看的,可是我听音频部分呀。

覆盖浏览器的 Ctrl-F 查找快捷键,并不提供方案来避免覆盖。我就搜索当前页面,不要你的站内搜索功能。

注册前请务必先阅读用户条款和规则,用户条款和规则页面需登录后才可访问。

简体中文内容指定繁体中文的字体,或者添加繁体中文的标签。或者反过来。

打扰用户

在内容页面,任何会动的非主体内容,包括但不限于广告、内容推荐。形式可以是动态 GIF、滚动动画、视频等。用于首页渲染效果的背景动画和视频不算,作为主体内容者也不算。

针对非音视频网站,自动或者非用户明确表达地(比如在用户点击不相关内容时)播放带音频的内容。

消耗 CPU 的背景特效。如 canvas-nest。会让 CPU 很吵,也会浪费能源、加剧气候变化。

by 依云 at August 18, 2022 09:14 AM

August 17, 2022

Alynx Zhou

阻止 clangd 污染项目根目录的一些方法

Emacs 的 lsp-mode 推荐使用 clangd 分析 C/C++ 代码,用起来体验还不错,但是让人非常恼火的是用户要主动或者被迫地在项目根目录下面添加一些文件,比如 .clang_complete 或者 compile_commands.json 来让 clangd 知道项目需要包含哪些库的头文件,以及 clangd 会直接把建立的索引丢到项目根目录下面的 .cache 目录里。虽然可以把这些加入 .gitignore,但保不齐哪个脾气古怪的上游维护者会和你纠缠半天让你解释为什么要加这些,实在是很麻烦。

compile_commands.json 比较好解决,clangd 提供了一个参数 --compile-commands-dir=<string> 可以指定查找这个文件的目录,我直接把它设置为 ./build/,因为大部分项目的 .gitignore 都会包含构建目录,也免得运行 bear -- meson compile 之后再把这个文件从 build 目录移出来。

(use-package lsp-mode
  :ensure t
  :commands lsp
  :hook ((c-mode . lsp-deferred)
         (c++-mode . lsp-deferred)
         (c-or-c++-mode . lsp-deferred)
         (lsp-mode . lsp-enable-which-key-integration))
  :bind (:map lsp-mode-map
              ("M-." . lsp-find-definition)
              ("M-," . lsp-find-references))
  :custom
  ;; Move lsp files into local dir.
  (lsp-server-install-dir (locate-user-emacs-file ".local/lsp/"))
  (lsp-session-file (locate-user-emacs-file ".local/lsp-session"))
  (lsp-keymap-prefix "C-c l")
  ;; Only enable log for debug.
  ;; This controls `*lsp-log*` buffer.
  (lsp-log-io nil)
  ;; JavaScript (ts-ls) settings.
  ;; OMG, the FUCKING EVIL SHITTY VSCode TypeScript language server generates
  ;; log in project dir, can MicroSoft stop to let their software put shit in
  ;; front of users?
  (lsp-clients-typescript-server-args '("--stdio" "--tsserver-log-file" "/tmp/tsserver-log.txt"))
  (lsp-javascript-format-insert-space-after-opening-and-before-closing-nonempty-braces nil)
  ;; Always let clangd look for compile_commands.json under build dir so it will
  ;; not make project root dirty.
  (lsp-clients-clangd-args ("--header-insertion-decorators=0" "--compile-commands-dir=./build/" "--enable-config")))

对于 .cache/ 就不是那么好解决了,根据 https://github.com/clangd/clangd/issues/341#issuecomment-1003560792,似乎他们并没有关闭或者修改缓存目录的支持。不过我想到一个弯道超车的方案,git 本身应该是有从其它位置加载用户定义的 gitignore 文件的功能的,我利用这个写一个本地的 gitignore 不就行了吗,搜索之后得到 https://stackoverflow.com/questions/5724455/can-i-make-a-user-specific-gitignore-file,操作起来也很简单。首先我把这个文件放到 ~/.config/git/gitignore,里面写上要忽略的 glob,然后运行 git config --global core.excludesfile ~/.cache/git/gitignore 就大功告成。

不过就在我写这篇文章时,clangd 的 issue 上有人回复我,根据 https://github.com/clangd/clangd/issues/184#issuecomment-998244415,现在 clangd 应该是会把索引放在 compile_commands.json 所在的目录,所以多少也算是解决了问题吧。虽然这样删掉构建目录之后索引缓存也没了,不过我觉得比起重建缓存,还是弄脏项目目录更恶心一点。

更新(2022-08-17):还有一个头疼的问题是 GLib 的 g_clear_pointer 宏里面使用到了对指针本体取 sizeof 的语法,而 clangd 默认会认为这是个错误,于是 lsp 就会标出一大堆问题。可以对项目进行设置关掉这一条,不过又会弄脏项目目录,查询文档得知 clangd 会读取 ~/.config/clangd/config.yaml 这个用户级别的配置文件,于是在里面写入内容关掉这条检查:

Diagnostics:
  ClangTidy:
    Remove: bugprone-sizeof-expression

然后给 clangd 传递 --enable-config 这个参数即可。

by Alynx Zhou (alynx.zhou@gmail.com) at August 17, 2022 08:28 AM

August 10, 2022

百合仙子

tmux 状态栏优化

本文来自依云's Blog,转载请注明。

在 tmux 的状态栏里,通常会显示当前时间。配置起来也非常简单,%Y-%m-%d %H:%M:%S这样的时间格式化字符串扔过去就可以了。然而这样做有个小问题:这个时间只能精确到秒。我的意思不是说我想让它显示毫秒,而是希望它像电视台和广播电台的时间一样,显示(播报)「12:00:00」的时候,就刚好是这一秒的开始。

一般来说,这么延迟个一秒以内的随机数问题不大,除了你有多个这种时间戳的时候——

tmux inside tmux inside tmux

这些时间戳哪个先更新、哪个后更新可完全说不准的,你可能看到明明在地球另一边的服务器上先到某一秒,本地才跟上。甚至同一个 tmux 的不同客户端里,这个时间戳的更新时间都可能会有差异。

我想优化这个的另一个原因是,我经常使用 extrace 来查看程序调用另一程序使用的命令行参数,然而我本地连了多少个 tmux,每秒便会有多少个 sh + awk 进程出来读系统负载。尤其是我从 Awesome 换到 Wayfire 之后,顶栏改用 waybar 了,很多指示器都是内建或者自己写的外部脚本,不再需要每隔几秒跑个子进程去获取信息,这样 tmux 调用子进程来刷新状态造成的干扰就突显了出来。

于是就有了 accurate-time 程序。它每个整秒会去读系统负载,然后和当前时间一起送给 tmux 来显示。每秒一个进程,已经少了很多啦。

既然是我的程序自己来读负载,也就方便做更多事情了,比如根据负载情况使用不同的文字颜色:绿色表示低负载,灰白是稍微有点活干,蓝色和 cyan 是比较忙碌,黄色、品红表示已经忙不过来啦,红色就是要累趴下啦。之前偶然间发现 qemu-git 这个包使用 ninja、但是链接的时候又套了一层 make,造成系统负载冲到了两百多。但是无论高低,tmux 的负载显示都是红色,所以我可能之前已经视而不见许多次了。加上颜色之后,这类异常就更容易被注意到了。以前我本地每次风扇呼呼地转才发现系统负载高,但是我要是用耳机的话就听不到了,现在也多了个高负载的指示。

安装和配置很简单,cargo build --release 编译,然后把编译出来的 target/release/accurate-time 扔到 $PATH 里,再如下配置 tmux 状态栏右边即可:

if-shell "accurate-time tmux" {
  set -g status-interval 0
} {
  set -g status-interval 1
  set -g status-right "#[fg=red]#(awk '{print $1, $2, $3}' /proc/loadavg) #[fg=colour15]%Y-%m-%d %H:%M:%S"
}

本来我还打算给 waybar 上的时间也这么做一下的,不过程序写好了才发现 waybar 自己已经把时间对齐到更新间隔了。

by 依云 at August 10, 2022 10:43 AM

Alynx Zhou

PHP 故释

// 新来的!如果你看到这段注释,说明上一个负责重构这个项目的程序员已经被气死了!
// 请你把下一行的数字加一,然后祝你好运!
// 63

起因是昨天晚上吃完饭回家路上和铁道迷闲聊说起他正在重写的 PHP 项目。于是我随口编了一个 恐怖 段子。

为什么是 63:

% node
Welcome to Node.js v18.7.0.
Type ".help" for more information.
> Math.floor(Math.random() * 100)
63
> 

by Alynx Zhou (alynx.zhou@gmail.com) at August 10, 2022 05:24 AM

August 08, 2022

百合仙子

Google Chrome 中的字体设置

本文来自依云's Blog,转载请注明。

Google Chrome 是我的备用浏览器,主要用于对比和检查网页的渲染效果,以及某些网页在火狐上可能不太正常,就用 Google Chrome 试试。

但 Google Chrome 有个非常令人恼火的问题:默认字体实在太难受了!

如果没有指明字体,那么 Google Chrome 默认使用 Times New Roman 字体。这是我十年前从 Windows 那边拷过来的字体,看上去细细软软的,很复古,不太适合屏幕显示。屏幕显示一般使用无衬线字体,但 Google Chrome 默认是衬线字体。好吧,那网页要是指明要 sans-serif 字体呢?Google Chrome 这次看上了 Arial,同样是一款古老的、来自于 Windows 的字体。

Google Chrome 默认使用 Windows 字体

行吧……反正我也不是多喜欢这两字体,就全部删掉好了!结果,呃,「自定义」??

Google Chrome 固执己见

实际上它们分别是「Liberation Serif」和「Liberation Sans」字体。这是我的系统上 fc-match「Times New Roman」和「Arial」给出的字体,由 ttf-liberation 包提供,google-chrome 包依赖(看 AUR 上的评论,这是因为 Google Chrome 的 PDF 渲染需要这个字体)。

Google Chrome 就是这么喜欢 Times New Roman 和 Arial,系统上不安装它也要找个替代品来用,就是不听用户通过 fontconfig 设置的默认字体。用户要想 Google Chrome 听点话,需要在「设置」->「外观」->「自定义字体」里像这样设置一下:

Google Chrome 使用 fontconfig 的设置方法

而火狐的话,用户不需要在这种犄角旮旯里设置:

火狐默认使用 fontconfig 的设置

可惜大部分用户都在用 Google Chrome 或其变种,所以制作网页上还是得手动指定一些现代点的字体。

by 依云 at August 08, 2022 04:05 AM

July 21, 2022

Alynx Zhou

从 PulseAudio 到 PipeWire

这篇的操作是在之前 运行在 JACK 上层的 PulseAudio 基础上进行的。

我自己的音频配置比较复杂,虽然 PipeWire 号称能兼容 PulseAudio 和 JACK 的 client 并且在某些发行版成为了默认选项,我还是没很快换掉。因为我想像之前用 PulseAudio 那样把 PipeWire 做成 JACK 的 client,虽然它的文档一直说支持这样,但是看起来两个月前代码才写好。于是我最近尝试了一下。

首先需要安装 pipewirepipewire-alsapipewire-pulse 和一个 PipeWire Media Session Manager,我用的是新的 WirePlumber 但是不要装 pipewire-jack 因为这个是模拟 JACK server 的。然后配置你的 session manager 开启 alsa.jack-device = true,然后理论上就能在设置里看到 JACK Sink/Source 了……但是……

首先装 pipewire-pulse 会替代 pulseaudio,但是按照上篇文章应该是安装了 pulseaudio-jack 这个依赖 pulseaudio 的包,解决方法是先卸载掉 pulseaudio-jack。然后继续安装重新登录应该 PipeWire 已经起来了,按理说这时候启动 JACK 就可以,但是不管我怎么搞都看不到 JACK Sink/Source,所以就备用方案,直接用 PipeWire 替代 JACK 看看,虽然早就可以这么做了,但是之前之前尝试 PipeWire 感觉不是那么稳定,所以就没一直用。

使用 PipeWire 当 JACK 的话要安装 pipewire-jack,和前面差不多的问题是 pipewire-jack 替代 jack2 但是 jack2-dbus 依赖 jack2,那就先删掉 jack2-dbus 再装就好了,然后重新登录,一切正常,Qjackctl 的 Graph 也能正常操作。而且比较有趣的是这样原本使用 PulseAudio 的程序也会在 JACK Graph 里面显示成节点(因为最后都通过 PipeWire),使用 Ardour 录音也没什么问题。其实我的需求还是比较简单的,也不需要什么太低的延迟,只是很多录音的程序都用 JACK 所以才要用。

用了一段时间之后感觉没什么问题,设备之间来回切换也没有卡顿了,驱动我的 2i4 也是完全正常,以后应该就先这样用了。

更新(2022-07-21):我发现在 Ardour 里面录音还是有问题,具体表现是录超过 1 分钟就会报 xrun,怀疑是这个 bug:https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/2257。不过其它 DAW 比如 REAPER 或者 Zrythm 都没问题,我倒是挺想换成 Zrythm 的,但是它还在 Beta 阶段。以及前面板插入耳机似乎设置里检测不到,但是开启一下 pauvcontrol 又能检测到了,总之是一些奇怪的小问题,也许我应该换回去。

更新(2022-07-21):我把我的 Sony Playstation Eye 拔掉之后似乎 Ardour 就正常了……我记得这个摄像头的麦克风阵列以前可以用的,不知道为什么现在 PulseAudio 都用不了了,所以看起来不是 PipeWire 的问题。至于为什么只有 Ardour + PipeWire 会出现这个问题,我猜是因为 Ardour 会连接所有可用的设备给自己用,于是就被这个不工作的设备影响出现延迟,而原版的 JACK 只会请求一个设备,Ardour 根本就看不到 PS Eye。也许我还是得买个正经的摄像头……

by Alynx Zhou (alynx.zhou@gmail.com) at July 21, 2022 09:16 AM

July 18, 2022

Alynx Zhou

可能只适合我自己的 RIME 配置 2

上一篇:可能只适合我自己的 RIME 配置

这一篇的原因是我最近在偶然间刷博客刷到一篇 讲 RIME 简体输入方案的文章,里面提到说朙月拼音因为是繁体转简体所以会出现各种错误(其实我个人倒是没怎么遇到过),然后推荐了一个完全针对简体字的输入方案 极光拼音,我自己其实只会输入简体字,不怎么需要输入繁体字的功能,所以打算试试。

已经有人在 AUR 打包了 rime-aurora-pinyin,所以我直接拿来用了,然后类似于我上一篇文章处理朙月拼音的办法,给这个也做了一些自定义设置,主要是添加 emoji,修改默认的全角标点上屏行为,以及加载扩展过的字典,不过遇到了几个问题。

首先是我像上篇文章说的那样直接在 patch 下面添加 __include: emoji_suggestion:/patch 并不能输入 emoji,我研究了很长时间,甚至以为 emoji 功能依赖繁体转简体。结果其实并不是,打开 emoji_suggestion.yaml 可以看到下面几句:

patch:
  switches/@next:
    name: emoji_suggestion
    reset: 1
    states: [ "🈚️️\uFE0E", "🈶️️\uFE0F" ]
  'engine/filters/@before 0':
    simplifier@emoji_suggestion
  emoji_suggestion:
    opencc_config: emoji.json
    option_name: emoji_suggestion
    tips: all

switches 的部分可以先忽略,关键在于 engine,这个 emoji 输入的原理是添加一个 filter,它接收一个输入,然后去附带的 opencc 的词典里查找这个输入得到对应的结果,再把这个输出给下一个 filter,按照词典,输入应该是中文字或者词,并且我看了一下词典,简体和繁体是都有的,所以也不存在简繁转换的问题。其实问题在于这段配置会把它作为第一个 filter 加入列表,而极光拼音的默认 filter 列表是这样的:

  filters:
    - uniquifier
    - charset_filter@gb2312
    - charset_filter@gbk

也就是说如果把 emoji 的 filter 加到第一个,它的输出就要继续经过 uniquifiercharset_filter@gb2312charset_filter@gbk,后两个是极光拼音为了排除掉几乎用不到的生僻字而添加的。而 emoji 显然不属于 gb2312 也不属于 gbk,自然就被过滤掉了。

所以我的解决方案是把 emoji 的 filter 加到列表最后,其实加到哪里无所谓,只要你确定前一个 filter 的输出是中文,能触发 emoji 的 opencc 词典就好了,我单独写了一个 emoji_suggestion.patch.yaml 文件:

switches/@next:
  name: emoji_suggestion
  reset: 1
  states: [ "🈚️️\uFE0E", "🈶️️\uFE0F" ]
engine/filters/@next: simplifier@emoji_suggestion
emoji_suggestion:
  opencc_config: emoji.json
  option_name: emoji_suggestion
  tips: all

导入的时候就写 __include: emoji_suggestion.patch:/。不过虽然我这个不再需要原来的那个 YAML 了,还是需要 rime-emoji 这个项目里其余的文件的。

顺便这也解释了为什么使用朙月拼音时候 emoji 后面的提示框显示的是繁体而非简体,因为朙月拼音从词库直接吐出来的是繁体,然后直接经过第一个 filter 就是 emoji,自然 emoji 查找时候用的就是繁体,然后才会经过简繁转换的 filter,所以如果把 emoji 的 filter 挪到简繁转换的 filter 后面,提示就会变成简体。

解决了 emoji 问题之后还有另一个问题,因为这个 emoji 的 filter 的输入是中文词组,也就意味着必须词库能吐出对应的中文词才能输入 emoji,比如说刚配置出来极光拼音的时候是吐不出来“笑哭”这个词的,所以就不会触发笑哭的 emoji。据说其它平台的输入法也有这个问题。其实没什么太好的解决方案,你可以说自己先手动打几次对应的词然后等 RIME 记住这个输入,不过我觉得也不太好。我想到的办法是既然需要词库里有,不如就让我用 emoji 的 opencc 词典生成一个 RIME 词库,然后扩展词库的时候加进去,这样无论如何都能吐出来了。其实也不是很麻烦,但是需要你把文字转成对应的拼音,那当然不能人工做这个操作了,我利用 Node 的 pinyin 库写了个脚本来做这件事:

#!/usr/bin/env node

const fs = require("fs");
const OpenCC = require("opencc");
const {pinyin} = require("pinyin");

// 我的词库只需要简体中文,如果你需要繁体中文,把 `t2s` 改成 `s2t` 应该就好了。
const converter = new OpenCC("t2s.json");

const outputFileName = "emoji_suggestion.dict.yaml";

const inputFileNames = [];

if (process.argv.length <= 2) {
  console.log(`Usage: ${process.argv[1]} file1 file2 ...`);
  process.exit(0);
}

for (let i = 2; i < process.argv.length; ++i) {
  inputFileNames.push(process.argv[i]);
}

const results = {};

for (const inputFileName of inputFileNames) {
  const words = fs.readFileSync(
    inputFileName, "utf8"
  ).split("\n").filter((line) => {
    return line.length !== 0;
  }).map((line) => {
    return line.split("\t")[0];
  });
  for (const w of words) {
    // rime-emoji 的 opencc 词典同时包含简体中文和繁体中文,但比如极光拼音
    // 这种默认不包含简繁转换的方案多半只想要其中一种,所以使用 opencc 对候选词
    // 进行一次转换。
    const word = converter.convertSync(w);
    if (results[word] != null) {
      continue;
    }
    const py = pinyin(word, {
      "heteronym": true,
      "segment": true,
      "style": "normal"
    }).map((array) => {
      // 有些时候就算利用结巴分词了,这个库仍然会没法判断多音字的读音然后丢出好
      // 几个结果,只取第一个好了。
      return array[0];
    }).join(" ");
    // 遇到处理不了的生僻字这个库会直接丢出原本的字……什么奇怪逻辑,只能判断是不
    // 是字母或空格了。
    if (/^[a-z ]*$/.test(py)) {
      results[word] = py;
    }
  }
}

const outputLines = [
  "# Rime dictionary for emoji",
  "# encoding: utf-8",
  "# Generated by `gen-emoji-dict.js` written by Alynx Zhou",
  "",
  "---",
  "name: emoji_suggestion",
  "version: \"0.1\"",
  "sort: by_weight",
  "...",
  ""
];
for (const k in results) {
  outputLines.push(`${k}\t${results[k]}`);
}
// console.log(outputLines.join("\n"));
fs.writeFileSync(outputFileName, outputLines.join("\n"), "utf8");

当然这个脚本不是很完美,比如 pinyin 识别不了的生僻字直接忽略了,不过我觉得它都识别不了,我多半也不会打出来的。然后虽然可以利用 jieba 分词提高多音字的准确性,还是有些不正确的,这些遇到了再手动纠错吧。

最后把这个词库添加进扩充词库:

# 原来要结合默认词库和第三方词库,
# 需要自己编写一个词库让它 fallback 到极光拼音和第三方词库。
# 我说佛老师对不起对不起,我不懂规矩。
---
name: aurora_pinyin.extended
version: "0.1"
# `by_weight`(按词频高低排序)或 `original`(保持原码表中的顺序)。
sort: by_weight
# 听说默认简化字八股文效果不好,还是算了。
# https://blog.coelacanthus.moe/posts/tech/a-new-rime-simp-pinyin-schema/
# 因为导入的朙月拼音词库是繁转简,所以这里不能导入简化字八股文。
# 导入简化字八股文。
# vocabulary: essay-zh-hans
# 选择是否导入预设词汇表【八股文】。
# use_preset_vocabulary: true

import_tables:
  # 主要是为了肥猫 wiki 词库。极光拼音好像是内置常用简化字表的。
  - zhwiki
  - aurora_pinyin
  - emoji_suggestion

顺便说一下我其实也不太了解这个扩展词库的顺序怎么设置比较好,不过我尝试的结果是像这样把 emoji 放在最后面,就不会每次输入在前面提示很多并不常用的 emoji 词组的问题。

我这个脚本生成的词库只有简体,不过我发现朙月拼音的简繁转换还是可以正常处理简体词库的,也就是说会变成 词库出简体 -> 简繁转换 -> 繁体变 emoji,所以直接加给朙月拼音也没问题,如果我需要用繁体中文,可以直接切换方案到朙月拼音(虽然实际上我的配置是简化字版,不过看起来主要区别只是默认是否开启繁体转简体)。平时输入简体则直接用极光拼音。

完整配置在 GitHub Repo 更新。

by Alynx Zhou (alynx.zhou@gmail.com) at July 18, 2022 10:50 AM

July 15, 2022

中文社区新闻

wxWidgets 3.2 更新可能需要手动干预

wxWidgets 3.2 在 GTK3 前端之外也提供了 Qt 前端,所以相关包的名称已从 wxgtk- 改名到 wxwidgets- 。不再提供 GTK2 前端。如果你安装过 wxgtk2 升级过程中可能遇到下述错误:

error: failed to prepare transaction (could not satisfy dependencies) :: removing wxgtk-common breaks dependency 'wxgtk-common' required by wxgtk2

这种情况下请删除 wxgtk2 并继续升级。

by farseerfc at July 15, 2022 10:32 PM

June 30, 2022

中文社区新闻

[archlinuxcn] 社区仓库现提供 debuginfod 服务

[archlinuxcn] 仓库现提供 debuginfod 服务(支持部分有调试符号的包),设置以下环境变量即可:

DEBUGINFOD_URLS="https://debuginfod.archlinux.org https://repo.archlinuxcn.org"

by lilydjwg at June 30, 2022 03:53 AM

June 22, 2022

Leo Shen

Use ZED with msmtp on Debian

Install msmtp and its mta. 1 apt install msmtp msmtp-mta mailutils Edit /root/.msmtprc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 defaults auth on tls on tls_trust_file /etc/ssl/certs/ca-certificates.crt logfile ~/.msmtp.log aliases /etc/aliases account alerts host SMTP_ADDRESS port 587 from SENDER_EMAIL_ADDRESS user SMTP_ACCOUNT password SMTP_PASSWORD account default : alerts Edit /etc/zfs/zed.d/zed.rc: Change ZED_EMAIL_ADDR to your desired mailbox

June 22, 2022 01:33 PM

June 12, 2022

Lainme

在CentOS7上安装H3C的iNode客户端

最近公司的VPN系统切换到了H3C的iNode,虽然H3C的网站上有安装手册,但那个安装过程繁琐且毫无必要,还会把系统弄的乱乱的,所以这里记录一下我自己的安装过程。

首先需要获取Linux版的软件,一般而言公司买了H3C的服务,都有相应的软件的,可以找IT要。如果要直接从H3C网站下载,可以从 https://www.h3c.com/cn/Service/Document_Software/Software_Download/IP_Management/iNode/iNode_PC/ 下载最新版。下载需要登陆,用户名和密码可以用 https://zhiliao.h3c.com/Theme/details/124921 链接中提供的那个(竟然真的可以登陆,我当时都震惊了……)

解压缩后在Linux目录下找到需要的版本,比如X86-64架构的就是“iNodeManager_H3C_Linux64_7.30(E0585).tar.gz”,解压缩后得到“iNodeManager”目录,进入这个目录执行安装脚本(其实大多数情况下没有必要……除非你的系统没有装过任何QT软件和7z才需要),

tar -xzf iNodeManager_H3C_Linux64_7.30\(E0585\).tar.gz
cd iNodeManager
bash ./install64.sh

再执行“iNodeManager”程序,

./iNodeManager

这个程序是对客户端进行定制的,完成后会生成客户端的安装程序,可以根据自己的需要勾选里面的选项。由于我们公司只用到了SSLVPN功能,我就只勾选了这一个,其他都不选(避免不必要的麻烦),如图所示,

点击右下角的“Finish”后会弹出新的对话框,勾选“Generate customized client setup program”,再点击“OK”。

完成后退出,并在iNodeManager目录下找到生成的“iNodeSetup”目录,进入这个目录解压自己需要的客户端版本,比如“iNodeClient_Linux64_7.3 (E0585).tar.gz”,得到“iNodeClient”目录,

cd iNodeSetup/
tar -xzf iNodeClient_Linux64_7.3\ \(E0585\).tar.gz

将这个目录移动到/opt下并用root权限执行安装脚本

sudo mv iNodeClient/ /opt/
cd /opt/iNodeClient/
sudo ./install_64.sh

安装完后就可以在系统菜单里找到iNode客户端。CentOS7要能正常启动客户端需要安装libpng12,别的系统没有测试。

sudo yum install libpng12

启动客户端后点击“+”添加链接,因为之前只选择了SSLVPN,所以协议选择的步骤直接“Next”就可以

其他连接参数的输入和Windows版差不多,这里就不写了。

by lainme (lainme@undisclosed.example.com) at June 12, 2022 11:52 PM

June 06, 2022

Phoenix Nemo

修复 CentOS 'org.freedesktop.login1' timed out

又到了喜闻乐见帮人修系统的时间。

根据情况描述是 “stuck at ‘Starting switch root’”,初步判定是 initramfs 坏掉了。经过询问果不其然,某个特别爱没事儿就更新的家伙在更新的时候被数据中心的智障远程手按了电源¯(ツ)/¯

既然启动不能那就进 Rescue 呗。虽然 CentOS 这系统不怎么样,但是至少也算在原版 ISO 就提供了 Rescue 的选项,还能自动寻找根分区挂载。本来以为小事一桩,准备 chroot 的时候突然冒出来一句 “chroot: cannot run command `/bin/sh’: Exec format error” 就瞬间心里一顿$#%#$^$#@#%#

一般来讲,报错 “Exec format error” 代表二进制格式错误,例如在 32 位系统中执行 64 位二进制文件。但这不可能,首先同样是 x86_64 架构,安装的系统和 ISO 的系统也是同样的版本,磁盘也没有报错,为什么会出现这种问题?

更新时断电导致文件被写坏的可能性并不是没有,但奇怪的是执行 /bin/bash 也报一样的错误。能坏到这种程度怕不是 glibc 挂了…?但总之第一步是要能先进系统瞧瞧究竟,于是心一横,直接用 Live CD 的 /usr 覆盖磁盘上的对应目录。

1
~> cp -r /usr /mnt/sysimage/

然后再 chroot,果然成功进入系统,yum 命令也可以用了。既然是更新过程中断电,那应该先尝试修复未完成的更新。

1
2
3
4
~> yum-complete-transaction
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
No unfinished transactions left.

啊咧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
~> yum update
--> Finished Dependency Resolution
Error: Multilib version problems found. This often means that the root
cause is something else and multilib version checking is just
pointing out that there is a problem. Eg.:

1. You have an upgrade for libselinux which is missing some
dependency that another package requires. Yum is trying to
solve this by installing an older version of glibc of the
different architecture. If you exclude the bad architecture
yum will tell you what the root cause is (which package
requires what). You can try redoing the upgrade with
--exclude glibc.otherarch ... this should give you an error
message showing the root cause of the problem.

2. You have multiple architectures of glibc installed, but
yum can only see an upgrade for one of those arcitectures.
If you don't want/need both architectures anymore then you
can remove the one with the missing update and everything
will work.

3. You have duplicate versions of glibc installed already.
You can use "yum check" to get yum show these errors.

...you can also use --setopt=protected_multilib=false to remove
this checking, however this is almost never the correct thing to
do as something else is very likely to go wrong (often causing
much more problems).

Protected multilib versions: glibc..... != glibc.....
kbd... x86_64 != kbd ... x86_64

…还真是。

进一步了解到这个系统并没有安装 32 位包,那么这个 32 位的 glibc 是哪儿来的呢?yum check 无果,更何况还有一个奇怪的 kbd 包甚至跟 32 位毫无关系。看来坏的不轻,但是既然确定系统中没有 32 位包,那么可以继续莽到底了。

1
2
3
4
5
6
7
# 完成所有未更新的包
~> yum update --setopt=protected_multilib=false
# 为重装系统做准备
~> mkdir /root/tmp
~> cd /root/tmp
# 重装所有包
~> yum reinstall * --setopt=protected_multilib=false

一通操作猛如虎,第一次重装所有包时还会报错 ldconfig 一些库是空文件,第二次完整重装就看不到报错了。看来可行,果断重启,一堆 OK 之后看到了 login 提示。

1
2
3
4
5
6
7
8
localhost login: root

Login incorrect

Login incorrect


Login incorrect

嗯嗯嗯???我还没输入密码呢???

还没输入密码就报错,至少不是 PAM 的问题。在那之前呢?应该是 systemd-logind 了。幸运的是网络可以正常启动,SSH 能够正常登入系统。如果网络或者 SSH 也不行,那就只好再次进 rescue 了。

系统里很多本来应该有的底层服务没有启动,例如 systemd-logind。想查看这货的状态,结果报错:

1
Failed to activate service 'org.freedesktop.systemd1': timed out

咦?systemd 自己都没正常启动,看看日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.hostname1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.hostname1.conf'
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.import1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.import1.conf'
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.locale1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.locale1.conf'
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.login1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.login1.conf'
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.machine1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.machine1.conf'
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.systemd1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.systemd1.conf'
localhost dbus-daemon[1166]: Encountered error 'Error in file /etc/dbus-1/system.d/org.freedesktop.timedate1.conf, line 1, column 0: no element found
' while parsing '/etc/dbus-1/system.d/org.freedesktop.timedate1.conf'
...

病的不轻。这些文件内容消失了,重装包并不会覆盖它们。只好手动来覆盖了。

1
2
3
4
5
6
7
# 安装必要工具
~> yum install yum-utils rpm2cpio
# 获取并解包 rpm
~> cd /root/tmp
~> yumdownloader dbus systemd
~> rpm2cpio dbus-1.10.24-15.el7.x86_64.rpm | cpio -idmv
~> rpm2cpio systemd-219-78.el7_9.5.x86_64.rpm | cpio -idmv

于是得到了这些包的内容。手动将 etc/dbus-1 目录下的所有文件覆盖到文件系统,然后执行 kill 1

1
2
3
4
5
6
7
localhost systemd[1]: Received SIGTERM from PID 4332 (bash).
localhost systemd[1]: Reexecuting.
localhost systemd[1]: systemd 219 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ
localhost systemd[1]: Detected architecture x86-64.
localhost dbus[1166]: [system] Successfully activated service 'org.freedesktop.systemd1'
localhost systemd[1]: Started Login Service.
localhost systemd-logind[4723]: New seat seat0.

仔细检查一遍,各种服务都在正常启动了!

(๑•̀ㅂ•́)و✧

后记:别没事儿更新生产用系统。

后记 2:西方国家的数据中心远程手没有一个靠谱的。

又到了喜闻乐见帮人修系统的时间。

June 06, 2022 03:22 AM

June 03, 2022

Felix Yan

萌新的 PolarFire SoC Icicle Kit 初体验

这两天翻出来了去年代收的 PolarFire SoC Icicle Kit。因为隔壁的 FPGA 大佬们看不上这块板子,我打算尝试物尽其用一下,目标只是用板子上的 RISC-V 核启动 Arch Linux RISC-V 的 rootfs 测试(把它当作一块 SD 卡槽没有问题、并且带 PCIE 的 HiFive Unleashed 来用。隔壁嵌入式群的大佬们:买椟还珠!)。如此便开始了年轻人的 FPGA 初体验(可能还是不能算)。

噩梦的开始

一开始尝试的当然是最新版的 Yocto 镜像,毕竟这是“官方”的 Linux 镜像。结果刷完后立刻遇到了启动失败:

一开始我还以为是 SD 卡坏了。在多次尝试未果后……

当时的猜测是(不一定对),可能因为板子上 FPGA 部分(抱歉,我不知道专业的称呼)不够新,所以我打算刷一下 HSS。结果这成为了噩梦的开始。

可怕的“硬件”工具链

我最初参考的文档来自 U-boot:https://u-boot.readthedocs.io/en/latest/board/microchip/mpfs_icicle.html

这份文档可能已经颇为过时,里面编译 HSS 的部分从一开始就找不到名叫“icicle-kit-es”的 BOARD.

在我加上 mpfs- 前缀,并根据后续报错依次按照我的 CROSS 工具链目标修改了 PLATFORM_RISCV_ABI=lp64d PLATFORM_RISCV_ISA=rv64gc 之后,我遇到了第一个大魔王:SoftConsole

好在这个工具可以无需注册直接下载。

顺利安装完成后,按照要求设置 SC_INSTALL_DIR,我终于看到了……下个错误:缺少 fpgenprog。

由于不想安装完整的 Libero SoC(一个巨大的需要折腾 license 的开发工具集),我试图去下载这里提到的 Program Debug Tool。从文档上找到下载链接点开后,看到了这样的悲剧:

它没了。

后面提到的 Programming and Debug 工具下载也藏在了注册墙后面,我考虑转向其他思路试试,先暂时没有继续了。

(另:关于 Libero SoC 的安装会有多坑,可以参考这位受害者的体验: https://www.cnx-software.com/2021/10/25/installing-libero-soc-in-ubuntu-and-windows-10/

Arch 内核的陨落

由于目前 Arch Linux RISC-V 基本没有处理 bootloader 部分的工作,我打算先试试直接使用旧版 Yocto 自带的 U-boot 来启动 Arch 的内核、rootfs。由于不熟悉 U-boot 和这些嵌入式镜像格式,我先花了点时间学习 U-boot 的启动逻辑 和操作 uImage 的常用命令等。

Yocto 的镜像里有一个很小的 /boot 分区,里面有 boot.scr.uimg 脚本和 fitImage 镜像。我遇到的第一个问题是,这个 FAT16 的 /boot 分区装不下 Arch 的 kernel,而且把 FAT16 扩容并转换为 FAT32 是 libparted 等常用工具不支持的操作。

在艰难尝试了许多次之后,我确定了这个分区后面的那个类型为 BIOS boot 但 GParted 抱怨为损坏分区的分区可能存放的是 uboot 本体(或者含它的 HSS?尚未仔细研究)。因此向后扩容 /boot 的想法可能不大靠谱。我只好退而求其次,把第一个分区删掉,直接把最后的 rootfs 所在分区标记为 legacy_boot

然后我复制了 Unmatched 上的 extlinux.conf 并做了相应修改(内核版本、UUID 等)。第一次尝试启动失败在缺少了两个环境变量:

后来在大佬们的帮助下,我找到了设置它的办法。其实正确的值就来自上面的 boot.scr.uimg,设置前者为 ${ramdisk_addr_r},后者则只需要设置一个足够大的值。

然而这次 Arch 的内核挂在了无限循环刷屏 L2CACHE ERROR 上。

由于大佬觉得这个问题不好解决,我决定先暂时放弃内核,用 Yocto 的内核启动 Arch rootfs 测试。

用魔法打败魔法

在现在的情况下,直接用原版的 boot.scr.uimg 和 fitImage 组合至少存在这样的问题:

  • 1、Arch rootfs 期待一个 rw 的 /。默认的 ro 环境会导致启动后一大堆服务失败、SSH Host Key 未生成等奇怪的问题。
  • 2、默认的 root= 设置为了 SD 卡的第三个分区(/dev/mmcblk0p3)。但经过上面的操作,SD 卡上现在已经只剩两个分区了。

为了修改 cmdline 解决这些问题,我在 U-boot shell 和 uEnv.txt 里鼓捣了各种操作都没有成功。看起来这个内核是把 cmdline 写死编译进去了。

在不重新编译内核的情况下,怎么更改里面写死的 cmdline 呢?那当然是直接修改二进制了!

首先把 fitImage 拆开,用 dumpimage 工具提取出里面的内核和 fdt 文件。从输出中可以看到,内核是 gzip 压缩过的:

用 gzip 解压这个文件,然后用 vim 打开:

诶嘿,我们的 cmdline 找到了。直接修改为想要的值(分区号直接替换,然后换掉一个应该影响不那么大的参数写上 rw),并保持字符串总长度不变(填空格补齐)。一共有两处,做同样处理即可。

修改完后,直接把未压缩的内核、fdt 复制到 rootfs 内,然后添加一个 extlinux 启动项:

label yocto
        menu label Arch Linux with Yocto kernel
        linux /boot/yocto.kernel.patched
        fdt /boot/yocto.dtb

Bingo!成功启动。

至此,最初的目标有了一个最低限度的成果。

尾声/题外话

感谢 PLCT 实验室、TUNA、AOSC 等社区一直以来的帮助,肥猫现在可能从完全不懂嵌入式稍微进步了一点点。

没想到折腾这块难啃的板子会成为我荒废三年多的博客再次更新的契机。我的这一轮 Arch Linux RISC-V 移植项目从最初尝试至今也已经超过三年,而今年年底就是我进入 Arch 十周年了。希望自己下次写博客不要又是三年以后 😛

The post 萌新的 PolarFire SoC Icicle Kit 初体验 first appeared on Felix's Blog.

by Felix Yan at June 03, 2022 07:26 PM

May 12, 2022

中文社区新闻

撤销以 wireplumber 替代 pipewire-media-session

两天前, wireplumber 新的打包会提示替代 pipewire-media-session 包,因为后者作为 PipeWire 的会话管理器,它的上游已经停止更新不再会有新版本了。不幸的是这个步骤有些仓促。
我们的 pipewire 音频包(pipewire-alsa, pipewire-jackpipewire-pulse)也提供了设置让 media-session 激活 PipeWire 的音频特性。如果没有按照这些包并且没有这些配置,PipeWire还是能用来处理屏幕录制而不干扰 ALSA 或 PulseAudio 。
WirePlumber 无视了这个机制,会总是试图让 PipeWire 接管音频设备,意味着 PulseAudio 和单用 ALSA 的用户的音频坏了。
已经撤销了包替换的修改,但是我们仍然在试图寻找一个更好的方案迁移到 WirePlumber 。如果你目前不使用 PipeWire 的音频功能,并且系统中已经安装了 wireplumber,请重新安装 pipewire-media-session 包后重启,以恢复音频功能。

pacman -Syu pipewire-media-session

by farseerfc at May 12, 2022 11:29 PM

May 11, 2022

Lainme

在CentOS7上安装最新版WPS

最新版的WPS需要GLIBC2.18以上,而CentOS7的版本是2.17,因此需要自行编译一个新的GLIBC。之前在CentOS 7上安装Dropbox这篇文章中写过如何编译和使用新的GLIBC,但当时写的方法不是很完全,这里再写一个更完整的版本。

依然先编译GLIBC,但最后一定要安装locale相关文件,不然WPS无法打开带有中文文件名的文件。

wget https://ftp.gnu.org/gnu/glibc/glibc-2.27.tar.gz
tar xzvf glibc-2.27.tar.gz
cd glibc-2.27
mkdir build
cd build
../configure --prefix=/opt/glibc-2.27
sudo make
sudo make install
sudo make localedata/install-locales

编译完成后将系统的库都链接过来,一劳永逸

cd /opt/glibc-2.27
sudo mv lib lib64
sudo mkdir lib
cd lib
sudo ln -s ../lib64/* .
sudo ln -s /lib64/* .

然后安装WPS并用patchelf修改WPS的各个可执行文件

cd /opt/kingsoft/wps-office/office6
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 et
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 wpp
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 wps
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 wpscloudsvr
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 wpsd
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 wpsoffice
sudo patchelf --set-interpreter /opt/glibc-2.27/lib/ld-linux-x86-64.so.2 wpspdf

by lainme (lainme@undisclosed.example.com) at May 11, 2022 10:35 PM

May 09, 2022

Leo Shen

Have fun with ZFS: Maintenance and Error Recovery

Now we have a pool, running smoothly. Let's learn a bit about regular maintenance, and what to do when things go wrong. Monitoring pool health The simplest way to check pool status is to use zpool status to view the pool status manually. This may be sufficient for small scale, non-critical systems; but for critical systems, we will want to know something went wrong the instant such event happens.

May 09, 2022 05:21 PM

中文社区新闻

QEMU >= 7.0.0 修改了拆包方式

从 qemu 7.0.0 更新开始我们用元包(meta package)将它拆包成了更细分的打包。

  • qemu包现在是被元包 qemu-base, qemu-desktopqemu-full 提供的虚包。
  • 7.0.0之前的qemu包的功能现在被qemu-desktop代替。
  • qemu-headless包的功能现在被qemu-base代替。
  • qemu-arch-extra包和qemu-headless-arch-extra包的功能现在被qemu-emulators-full代替。
  • 元包qemu-full包提供所有 QEMU 相关的包(除了qemu-guest-agent)

by farseerfc at May 09, 2022 10:53 AM

May 07, 2022

Alynx Zhou

装了台 NAS

本来我是不打算装 NAS 的,甚至都把我的星际蜗牛关了,因为我觉得我又没有网络多人协作的需求,而且我醒着的时候我的台式机也醒着,我睡着了又不会用到网络存储。不过自从我买了相机开始拍照片录视频,存储空间就越来越紧张了,先是把我机箱里的硬盘从 2T 直接升级到 8T,又觉得没有冗余始终心慌慌。偏偏我现在这个机箱哪里都不错,就是机械硬盘位不太充裕。为了扩展存储空间,也只能装一个 NAS 了。

决定好装 NAS 之后比较难的就是选硬件了,我肯定不会买那些闭源拖拉机的,我要装 Linux。肯定排除掉星际蜗牛,因为我实在不放心用那个背板带四块硬盘。然后对这种低功耗的设备用那种主板和 CPU 集成的赛扬应该不错,还免了主动散热,问题是我不知道在哪里能买到。所以还是考虑普通零售的硬件,不过大部分零售的硬件都有点性能过剩了,挑来挑去挑出下面的一套配置,比较个人倾向,不建议大家直接拿过来用。

  • CPU:i3-10105T 645
  • 主板:七彩虹 B460I 579
  • 机箱:乔思伯 N1 669
  • 电源:银欣 SX500-LG 549
  • 散热:利民 AXP90-X47 139
  • 内存:光威 8G DDR4 2666 169x2
  • 网卡:EDUP PCI-E 2.5Gbps 网卡 89
  • SSD:闪迪 至尊高速 NVMe 256G 259
  • HDD:西数 HC550 16T 1498x4
  • HDD:希捷 酷鹰 2T 375
  • 扩展:乐扩 M.2 A+E Key 转 SATA 75

下面简单介绍一下为什么选这套。

首先装 NAS 第一个要决定的不是别的而是硬盘,毕竟你这设备就是拿来放硬盘的,因为台式机里面已经有 8T 的硬盘了,我也不想再经历换硬盘时候拷贝数据的痛苦,所以还是直接上了 16T 的。这种容量建议直接购买企业盘,不过也就和静音说拜拜了。没选希捷的银河系列不是因为别的,只是因为西数 HC 系列便宜点而已。选 4 块盘而不是 3 块的原因也很简单,我不会用 ZFS,肯定要用 btrfs,但是 btrfs 的 RAID5 有 bug,于是性能容量冗余的平衡点就是 RAID10 了,那就需要 4 块硬盘起步。

决定好硬盘之后就得选合适的机箱,毕竟你得能把硬盘装进去,不过我对大部分的 NAS 机箱都不满意,有些类似个人产品或者众筹的机箱比如什么宝藏盒TANK之类的看起来很能装,但是造型不伦不类,做工也一般般,然后可能迎广或者万由有一些成品 NAS 机箱看起来不错,但是基本上是高配蜗牛,而且我实在不想用 flex 电源。其实某种意义上我是先看中乔思伯 N1 这个机箱才决定装的 NAS,我对这个牌子其实没什么好感,但这个机箱实在是过于好看,而且可以装 SFX 或者 SFX-L 电源,还是有很多零售产品可以选的。

选好机箱以后就是机箱里能塞进什么硬件就塞什么硬件了,乔思伯 N1 和大部分 NAS 机箱一样都只能放 ITX 主板,但 ITX 主板可选的实在不多。首先我想要一个内置核显的 CPU,因为最简单的调试办法肯定是给机箱接上键盘鼠标显示器,也比较方便调整 BIOS,而 ITX 只有一个宝贵的 PCIE 插槽,再说 NAS 多半也没必要上个亮机卡耗电。而 AMD 那边 200GE 太弱了,Ryzen Pro 系列又太强了,实在不知道选什么比较好,而且也不容易买到最低配的 Ryzen Pro CPU,所以就选 Intel i3 了,正好还有带 T 的低功耗版本。同时 10 代的主板还比较好买,也就是说能买到新的。虽然这台机箱前面有 USB-C,不过实际上是和 USB 3.0 共享一个插座,所以也不需要主板有 USB-C 的插座。七彩虹这块 B460 的优点在于后面还是有一个标准的 USB-C 接口,以及能在京东自营买到。

SFX 电源虽然不如 ATX 电源好选,但是仍然比 flex 电源好选太多了,而且这台机器还可以放下 SFX-L 的电源,12cm 的风扇肯定比 4cm 要安静很多。于是我就在京东随便挑了一个观感不坏的牌子的全模组 SFX-L 电源里功率最低的。不过装这个机箱的时候最好注意一点,买的电源的 24pin 线最好不要是那种捆成圆柱型的,因为需要从主板边上极小的缝隙里拉过来,排线肯定比圆柱线要容易过,银欣这款是排线。当然你是定制模组线的有钱人当我没说。

这款机箱最高可以支持 70mm 的下压散热,所以其实完全可以买利民那款 12cm 的散热器,不过我觉得也无所谓了,我比较担心 12cm 的下压散热会导致在主板上装内存和 SSD 比较困难,9cm 的也够用了。

内存其实是随便选的,因为我不用 ZFS,其它文件系统对于 ECC 也没那么严格的依赖。然后 i3 和 B460 也不会支持更高的频率了。

考虑到我的台式机上有两个网口,分别是 1Gbps 和 2.5Gbps 的,我就在想可不可以把 NAS 和台式机通过 2.5Gbps 直接连起来(路由器什么时候普及 2.5Gbps 啊),这样速度基本上和访问本机的机械硬盘没有太大差距。但是主板厂家实在是太抠门了,本来 ITX 主板型号就不多,内置 2.5Gbps 网卡的就更少了。虽然让一个 2.5Gbps 的网卡占着 PCI-E x16 插槽实在是有点浪费,但也没别的选择了。至于万兆,考虑到要使用专门的线和专门的网卡,还得给台式机也装一个,算了吧,反正机械硬盘的阵列也跑不满万兆。

然后系统盘只要随便搞一个 NVMe 就可以了,反正安装一个 Arch Linux 不会占用多少空间。实际上比较头痛的是怎么接上所有的硬盘。这款机箱有 5 个在背板上的 3.5 寸硬盘位和一个单独的 2.5 寸硬盘位。大部分 ITX 主板都只有 4 个 SATA 接口,本来我觉得接 4 盘的阵列够了,但是后来我发现北邮人 pt 可以使用我的交大学校邮箱注册……要是挂 PT,我觉得还是单独放一块硬盘比较好,那接口就不够用了。比较靠谱的办法是买 HBA 卡把 PCI-E 转成 SATA,但是已经被 2.5 Gbps 网卡用掉了。我还没有发现同时有网卡和 SATA 的 PCI-E 扩展卡。还有一个方案是 M.2 转 5 个 SATA 的转接卡,其实倒也可以用一块 2.5 寸的 SSD 当作系统盘,然后解放出这个 M.2 接口,不过我实在不放心把 4 盘阵列放在这种转接卡上……而且我估计系统盘也是不能接在转接卡上的。当然你可以买一块有 2.5 Gbps 网卡的 ITX 主板,不过那基本只有 Z590 可选了,最便宜的华擎 Z590 ITX 也要 1400。所以我最后采用的方案是放弃掉无线网卡,反正 NAS 摆在路由器附近可以拉网线,然后把无线网卡下面那个 A+E Key 的 M.2 接口利用上。有这样的转接卡,可以转接出两个 SATA 口,虽然我只要一个就够了。

不过当我拿到转接卡的快递之后发现安装还是有点麻烦。最好的办法是拧下无线网卡之后发现那个保护罩可以拆掉一侧的面板,然后可以把无线网卡从保护罩上拿下来再换成转接卡。但是那个固定无线网卡的螺丝实在太紧了我拧不下来,只能放弃这个保护罩,反正转接卡应该也不会受到什么外力,就在我把转接卡插好打算开机之前,我发现还是有点问题……虽然 SATA 接口已经够矮了,但是里面那个用不上的 SATA 接口还是顶住了前面的音频接口,导致转接板是歪的。其实可以淘宝再买一个 M.2 A+E Key 的延长线,不过那样我纠结的就是把延长出来的转接卡固定在哪里了。不管了!反正我就要一个 SATA 口,用刀把里面的 SATA 口的顶端削掉吧!虽然削的很粗糙但是还是达到了效果,然后用一块胶带把那个口粘住,防止 SATA 接口里面的金属触点接触到音频口保护壳而短路,再把一块泡沫放到转接板后面顶住转接板,我觉得就差不多了。

直接装上是歪的:

1.jpg

“高个的 SATA 接口被我给锯了,比矮个的还矮!”:

2.jpg

“磨”改之后的效果,应该没问题吧:

3.jpg

最后就是绞尽脑汁把各种线塞到 ITX 机箱里面,实在是太痛苦了。对了,这个机箱自带的风扇接线不是很长,而它那个硬盘背板上的插针又是满速的,如果你要是想插到主板上进行调速,最好自备一根 4pin 风扇延长线。

剩下就没什么好说的了,毕竟我不打算在这里复述一遍 Arch Wiki。我对 btrfs 使用的参数是 -m raid1c3 -d raid10,然后在系统里设置了 samba 和 transmission 的daemon。最后附上一些装完的照片。

4.jpg

5.jpg

6.jpg

by Alynx Zhou (alynx.zhou@gmail.com) at May 07, 2022 09:49 AM

April 09, 2022

Leo Shen

Have fun with ZFS: Tuning

We now have a working ZFS pool. Now, we will do some tweaking so that our pool can perform as good as possible. Disable atime (access time) By default, ZFS records the latest access time of a file. In many circumstances, this isn't actually useful and negatively impact performance. If you're not using applications that rely on access time (notable example being some mail clients, which use atime to see if mail has been read), we can turn it off to reduce disk writes.

April 09, 2022 06:25 PM

April 05, 2022

Lainme

给手机设置受限的SFTP服务器

准备允许自己的手机通过SFTP读写服务器上的一些文件,但无论是密码登陆还是密钥登陆都不能让我很放心,所以做了一些额外的配置并且要求

  • 手机只能登陆SFTP不能登陆SSH,且只能读写指定的目录
  • 不能影响任何现有的功能,比如我桌面端的登陆

先给手机建立一个专门的目录,目录及上级目录都要是root所有(为chroot做准备)

sudo mkdir /home/mobile

然后将所有允许读写的目录都mount bind过来。比如目录/home/[用户名]/Documents

sudo mkdir /home/mobile/Documents
sudo chown [用户名]:[用户名] /home/mobile/Documents
sudo mount --bind /home/[用户名]/Documents /home/mobile/Documents

然后设置SSH服务器如下(只显示关键配置)

Port 22
Port 2333

Subsystem sftp internal-sftp

Match LocalPort 2333
        AllowGroups [用户名]
        AllowUsers [用户名]
	X11Forwarding no
	AllowTcpForwarding no	
        AuthorizedKeysFile /home/[用户名]/.ssh/authorized_keys_sftp
	ChrootDirectory /home/mobile/
	ForceCommand internal-sftp

这里除了原本的22端口额外设置了一个2333端口,并且在连接这个端口的时候用authorized_keys_sftp来验证,chroot到/home/mobile目录,并且只允许SFTP。

然后重启SSH服务器

sudo systemctl restart sshd

在防火墙上放行2333端口,并将一组密钥对的公钥放到/home/[用户名]/.ssh/authorized_keys_sftp,私钥放到手机连接SFTP的APP上就可以连接了。密钥也可以设置Passprase。

by lainme (lainme@undisclosed.example.com) at April 05, 2022 07:41 PM

April 04, 2022

Lainme

用Syncthing进行文件同步

Syncthing是点对点的开源文件同步软件,在Linux上只需要将软件下载下来解压缩执行就可以直接使用,但这样设备的发现和数据中继都是用的官方提供的服务器,如果想要信息和数据更为可控可以搭建自己的发现和中继服务器。另外,虽然syncthing不需要中心客户端,但为了能时刻保持数据的完整以及简化多设备之间的同步,最好还是能有一个中心客户端作为核心24小时在线。

如果完全采用中心客户端的形式(即所有从属客户端都只和中心客户端连接,相互之间不连接),并且中心客户端安装在服务器上,也可以不要发现和中继,直接用IP地址直连。

准备工作

以Debian/Ubuntu为例,可以直接添加官方APT源来安装。

添加APT源的key

curl -s https://syncthing.net/release-key.txt | sudo apt-key add -

添加APT源

echo "deb https://apt.syncthing.net/ syncthing stable" | sudo tee /etc/apt/sources.list.d/syncthing.list

更新一下

sudo apt update

发现服务器

安装发现服务器

sudo apt install syncthing-discosrv

默认的设置可以直接使用,所以激活和启用服务就行了

sudo systemctl enable stdiscosrv
sudo systemctl start stdiscosrv
sudo systemctl status stdiscosrv

服务启动后会打印出设备ID(用sudo systemctl status stdiscosrv就能看到),只需要在防火墙上打开8443端口,就能用

  https://[服务器IP]:8443/?id=[设备ID]

作为发现服务器使用了。

中继服务器

安装软件

sudo apt install syncthing-relaysrv

中继服务器需要修改一些配置,编辑/etc/default/syncthing-relaysrv,修改如下

# Default settings for syncthing-relaysrv (strelaysrv).
NAT=false

## Add Options here:
RELAYSRV_OPTS=-pools=""

这会禁用UPnP/NAT-PMP,同时将中继服务器设置为私有(不加入公共池)。

激活和启用服务

sudo systemctl enable strelaysrv
sudo systemctl start strelaysrv
sudo systemctl status strelaysrv

和之前类似,服务启动后会打印出中继服务器的完整地址,例如

relay://0.0.0.0:22067/?id=[设备ID]&pingInterval=1m0s&networkTimeout=2m0s&sessionLimitBps=0

不过里面的IP是“0.0.0.0”,后面的参数一般也不需要。在客户端填中继服务器信息的时候只需要以下形式。当然,22067的端口也需要在防火墙上打开。

relay://[服务器IP]:22067/?id=[设备ID]

中心客户端

同样从APT源安装

sudo apt install syncthing

然后激活和启用

sudo systemctl enable syncthing@[用户名].service
sudo systemctl start syncthing@[用户名].service
sudo systemctl status syncthing@[用户名].service

这里的用户名最好不要用root,一般而言是你希望放文件的用户名,最好和你其他客户端的用户一致(对于Linux而言不一致可能有账户权限问题)。客户端启动后也会显示出设备ID,可以填入其他客户端中进行同步。同时最好能在防火墙和路由器上打开22000端口,以便能够直连。

客户端启动后需要在Web界面做一些配置。如果中心客户端是安装在服务器上的,可以用SSH隧道把客户端的端口绑定到本地,就能用本地浏览器打开设置了。绑定的方法是

ssh -L [本地想用的端口]:localhost:8384 [服务器地址]

本地端口一般不要是8384,否则会和本地的客户端冲突。绑定后在本地浏览器输入

http://localhost:[本地想用的端口]

就可以打开服务器上的客户端界面。

安装在服务器上的中心客户端因为必然可以直连,可以不做什么设置,禁用Global Discovery和Enable Relaying就行。

如果是其他机器,基本设置如下:

  • 激活Global Discovery,但将地址改成自己的。
  • 激活Enable Relaying,但将监听栏的地址改成
tcp://0.0.0.0:22000, [你自己的Relay地址]

从属客户端

在自己的其他设备上安装syncthing客户端,设置上和中心客户端一样,然后添加中心客户端。如果中心客户端是安装在服务器上的,添加的时候可以把地址从dynamic改成tcp://[服务器IP]:22000

by lainme (lainme@undisclosed.example.com) at April 04, 2022 03:47 PM

April 01, 2022

百合仙子

从 getmail6 到 offlineimap

本文来自依云's Blog,转载请注明。

起因

上个月收到这样一封邮件:

自 5 月 30 日起,您可能会无法再访问那些采用安全性较低的登录技术的应用

意思就是说,Google 觉得把密码直接交给邮件客户端,权限太大,不够安全。所以要用户改用基于 OAuth2 的认证方式,只给程序邮件相关的权限。哦,你说应用专属密码?要用那个必须得启用两步验证——也就是意味着遇到灾难的话,我无法从一无所有的状态开始恢复。

从 POP 到 IMAP

getmail6 只支持使用 XOAUTH2 认证的 IMAP 协议,并不在 POP 协议上支持这个(不知道是否有可能)。所以我得换 IMAP 协议了。

具体操作步骤在 getmail6 的示例配置中有写。简单来说就是自己去申请个桌面软件的 app 信息,然后给自己的用户添加试用权限,再通过 OAuth2 获取 refresh token 和 access token,就能登录了。getmail6 自带了个 getmail-gmail-xoauth-tokens 程序用来走 OAuth2 流程,不需要另外安装程序来处理的同时也可以给其它程序使用。

所以我的 msmtp 配置就不用麻烦了,改两行配置就好:

auth oauthbearer
passwordeval getmail-gmail-xoauth-tokens ~/.getmail/gmail/lilydjwg@gmail.com.json

但是呢,虽然邮件是收回来了,IMAP 和 POP 还是挺不一样的。POP 没有「文件夹」的概念,所有收到的邮件,不管我有没有在 Gmail 网页或者客户端上阅读、归档,不管它进了哪个标签(文件夹)(「垃圾邮件」除外),我都会收到,并且把收过的邮件标记为已读。

而通过 getmail6 使用 IMAP 收取,我能做的选择就是,要不要把收过的邮件标记为已读或者删掉(可在 Gmail 中设置为归档)。不管如何,getmail6 只会收到它运行时位于收件箱中的邮件。如果我选择标记为已读的话,那么已读邮件也不会被 getmail6 收到。所以标已读的话,我在别的地方看过的邮件不会被收到。删除的话会好点,收过的邮件归档了,还省得我手动去归档,但是在别的地方,收过的邮件和已处理的邮件没了区分。

所以不如上 offlineimap,完全同步好了。

从 getmail6 到 offlineimap

offlineimap 的配置就比较复杂了,一是要对文件夹名进行转码,二是我要设定只同步指定的文件夹:收件箱、Maillist 和垃圾邮件。要同步垃圾邮件的原因是,Gmail 经常把有用的邮件往里边扔。

[general]
accounts = gmail
maxsyncaccounts = 10
socktimeout = 60
pythonfile = ~/.offlineimap/offlineimap.py

[Account gmail]
localrepository = gmail-local
remoterepository = gmail-remote

[Repository gmail-local]
type = GmailMaildir
localfolders = ~/.Maildir
filename_use_mail_timestamp = no
nametrans = gmail_nametrans_local

[Repository gmail-remote]
type = Gmail
remoteuser = lilydjwg@gmail.com

sslcacertfile = /etc/ssl/cert.pem
ssl = yes
starttls = no

oauth2_client_id_eval = get_client_id("lilydjwg@gmail.com")
oauth2_client_secret_eval = get_client_secret("lilydjwg@gmail.com")
oauth2_access_token_eval = get_access_token("lilydjwg@gmail.com")

nametrans = gmail_nametrans_remote
folderfilter = gmail_folderfilter
import os
import json
import subprocess

_LOADED_DATA = {}

def _load_data(account):
  with open(os.path.expanduser(f'~/.getmail/gmail/{account}.json')) as f:
    _LOADED_DATA[account] = json.load(f)

def get_client_id(account):
  if account not in _LOADED_DATA:
    _load_data(account)
  return _LOADED_DATA[account]['client_id']

def get_client_secret(account):
  if account not in _LOADED_DATA:
    _load_data(account)
  return _LOADED_DATA[account]['client_secret']

def get_access_token(account):
  cmd = [
    'getmail-gmail-xoauth-tokens',
    os.path.expanduser(f'~/.getmail/gmail/{account}.json'),
  ]
  out = subprocess.check_output(cmd, text=True)
  return out

def gmail_nametrans_remote(foldername):
  foldername = foldername.removeprefix('[Gmail]/').encode('ascii').decode('imap4-utf-7')
  if foldername == '垃圾邮件':
    foldername = 'Spam'
  elif foldername == '草稿':
    foldername = 'Drafts'
  return foldername

def gmail_nametrans_local(foldername):
  if foldername == 'Spam':
    foldername = '[Gmail]/垃圾邮件'
  elif foldername == 'Drafts':
    foldername = '[Gmail]/草稿'
  return foldername.encode('imap4-utf-7').decode('ascii')

def gmail_folderfilter(foldername):
  foldername = foldername.encode('ascii').decode('imap4-utf-7')
  return foldername in [
    'INBOX', '[Gmail]/垃圾邮件', '[Gmail]/草稿',
    'Maillist',
  ]

然后在 Gmail 那边创建个过滤器,把来自邮件列表的邮件扔到「Maillist」文件夹里去。搜索「 (to:@googlegroups.com OR from:vim-dev-github@256bit.org OR to:@zsh.org)」并创建过滤器,选择操作「跳过收件箱、 应用标签“Maillist”」即可。注意以后在修改的时候直接修改「包含字词」字段即可,并且记得「OR」「AND」「NOT」之类的操作符需要改回大写。

这样做完之后还有个问题:一封邮件同步到 offlineimap 后,我在 mutt 里阅读并删掉了它。offlineimap 一看,哟,邮件没了,得在服务器上删掉。Gmail 根据我的设置,把从 IMAP 删除的邮件归档,但是它并没有选项来标记为已读。所以这封邮件最终会以未读的状态躺在「所有邮件」里。

于是我去 App Script 里写了个脚本,把这些邮件标记为已读:

function mark_as_read() {
  const threads = GmailApp.search('is:unread AND NOT (label:Maillist OR in:inbox)', 0, 30)
  for(const thread of threads) {
    Logger.log('Marking as read: %s', thread.getFirstMessageSubject())
    thread.markRead()
  }
}

手动运行一遍之后,就可以在左侧栏里给它设置个触发器定时跑啦。

新邮件提示

使用 offlineimap 之后,最大的问题变成了邮件散落在不同的账号下的不同文件夹,一个个过去翻看太低效了。所以我就给 zsh 设置了提醒:

mailpath=(
  ~/.Maildir/INBOX/new'?GMail has a new message.'
  ~/.Maildir/Spam/new'?GMail has a new spam.'
  ~/.Mail/inbox'?New local mails.'
)

问号前边是邮箱的路径,后边是提示信息。之前那个 mbox 格式的邮箱我还留着,用来收取来自本地 cron 的邮件。

一个小问题是,procmail 用不成了。不过现在各种无用的网站消息也少了,所以不需要通过 procmail 处理垃圾邮件了(新浪微博我没有使用邮件注册、LinkedIn 和 Twitter 消停了、网易和QQ邮箱不用了)。现在中文邮件列表也几乎没人用了,我也不用让程序去重写「回复:RE:回复:」这类糟糕的邮件标题和过滤掉自动回复了。

by 依云 at April 01, 2022 06:29 AM

March 26, 2022

百合仙子

微信消息通知的困扰

本文来自依云's Blog,转载请注明。

一直以来,不得不用的微信以其糟糕的通知体验让我十分不爽。

在手机上,我使用的是 Google Play 商店里的微信。在电脑上,我使用的是通过 Wine 运行的 Windows 版本微信([archlinuxcn] 仓库里的 wine-wechat-setup 脚本可用于安装)。

消息通知不及时

这个问题是最近我的手机日渐陈旧之后我才注意到的。表现是,在一段时间(比如一两天)不使用微信之后,收到新的微信消息或者视频通话,可能会延迟几个小时收到通知。在 Android 通知日志中可以确认,收到通知的时间和消息在微信中展示的时间有数小时之差,并不是因为我没有及时看手机。

我的 Telegram 从来不这样丢消息,即使因为后台进程过多 Telegram 被杀之后,通知只会不能在其它端阅读之后被清除,而不会延迟那么久。而微信,即使它还在后台运行着,却经常占着资源不干活,何况消息通知本应走 FCM。

打开微信即清除所有通知

我有一条微信消息,但是我现在不方便立即回复(比如需要使用电脑而我正出门在外),所以我会让那条通知一直留着。其实以前版本的 Android 系统更方便,可以将通知延后一段时间,只是不能自动指定延后的时间比较遗憾,后来被移除真的太可惜了。

然后呢,比如我要进个超市,或者测个核酸,付个钱啥的,只好打开微信扫码呗。结果所有还未处理的通知全部不见了!等忙完当时的事情,回到家里的时候,我就不一定还记得我还有几条微信消息还没处理了。

多端不同步容易错过

我在用电脑的时候,如果不登录电脑版微信的话,那么我将不会注意到手机上的微信有新消息。那就登录电脑版吧,然后我去上个厕所吃个饭,不用电脑的时候又会错过消息。在电脑端登录的时候让手机上也显示消息通知?那样所有消息都要看两遍,而且消息多的时候还得仔细回想某条消息到底是不是已经处理了。

Telegram 的消息同步做得真好啊。你在哪端用,哪端先给你发通知。其它端的消息会晚几秒出现。一旦在任意端读取了消息,另外的端上的相应消息全部都会被取消掉,不会有消息重复的问题(不过最近好像 Android 上的通知取消变得不那么可靠了)。

手机不可离远

现在电脑版微信终于不需要天天在手机上确认登录了。只要我每两三天登录一次,就可以避免把手机扔去充电了、来到电脑前、又去找手机的麻烦事。——我一开始是这么以为的。直到我发现,我刚刚看到通知、正要处理的消息,并没有在电脑版微信里同步出来。

不知道为什么,微信跟电脑用户有仇似的。明明我有电脑了,不需要凑合于手机的小屏幕和戳戳戳的屏幕键盘了,微信还非得把手机给拉过来。

语音通话不支持耳机接听

某天,我因为沉迷于放置型游戏,把手机扔桌子上充上电让它自个儿玩,自己去睡觉了。第二天早上睡正酣的时候,来了个电话,我拿耳机给接了。然后需要通话的另一方不知道怎么想的,没有商量就发起了微信语音通话,这个耳机根本接不到……

我也尝试过让 Google 助理回拨电话,不过使用不熟练,并没有成功。你说我为什么不起床去接?我睡着被电话惊醒了,还没回过神来啊 QAQ。

by 依云 at March 26, 2022 09:55 AM

March 25, 2022

中文社区新闻

Keycloak 17.0.1-2 更新需要手动重新配置

keycloak包在17.0.1-2版本前跑了WildFly服务器。因为上游在正式迁移到Quarkus发布,Arch Linux 也将跟从这一决定。这意味着升级时需要一些手动干预。
配置文件需要从老的 .xml 格式更新到新的 /etc/keycloak/keycloak.conf
在更新之前,停止 keycloak 服务,升级包,并且在开启服务前迁移配置:

systemctl stop keycloak.service
pacman -Syu keycloak
# migrate configuration /etc/keycloak/keycloak.conf
systemctl start keycloak.service

参阅 Keycloak 的 迁移文档 和 Keycloak Quakus 服务器文档

by farseerfc at March 25, 2022 11:53 PM

March 23, 2022

Alynx Zhou

March 18, 2022

Leo Shen

Have fun with ZFS: Setting up storage pool

Now we've explored the benefits and limitations of ZFS, let's create a pool and try it out! This guide will not explore how to use ZFS as root, as it is very distro/OS-dependent. View guides about this topic on your distro/OS's own Wiki or Handbook. Concepts Unlike filesystems that operates on a single disk, partition or logical volume (i.e. ext4 and NTFS), ZFS can span across multiple storage volumes.

March 18, 2022 03:49 PM

March 08, 2022

中文社区新闻

Arch Linux 项目负责人竞选结果

本月我们举行了我们的负责人竞选,结果是没有对我们的当前负责人 Levente Polyák 的异议。根据我们的 选举规则,他将继续连任一个新任期。

恭喜 Levente Polyak 赢得新任期!

by farseerfc at March 08, 2022 02:05 PM

百合仙子

Qt 的字体渲染问题

本文来自依云's Blog,转载请注明。

GUI 程序我现在依然倾向于 GTK,因为虽然 Qt 拥有良好的跨平台性,但可能是太注重跨平台性了,在 Linux 平台上反而有一些水土不服的问题。

字体太多,支持太少

你可能觉得,系统上字体太少,所以经常会遇到不常见的字符无法显示的情况。然而对于 Qt 来说,字体越多,反而越容易遇到个别字符不能显示的情况。

这是我的 /etc/fonts/conf.d/66-qt.conf 中的一段。因为顺序的原因,我只能放到 /etc 下。除了针对 sans-serif 配置外,我也有同样的配置应用于 serif 和 monospace。

<fontconfig>
  <!-- Adjust font order for Qt applications -->
  <alias>
    <family>sans-serif</family>
    <prefer>
      <!-- 格拉哥里字母:Ⰽⱁⱀⱄⱅⰰⱀⱅⰹⱀ Ⰹⱍⰹⰳⱁⰲ -->
      <family>Noto Sans Glagolitic</family>
      <!-- 爪哇文:꧁   ꧂ -->
      <family>Noto Sans Javanese</family>
      <!-- 西夏文:𗷲𗒅 -->
      <family>Noto Serif Tangut</family>
      <!-- 埃及象形文字:𓁹 -->
      <family>Noto Sans Egyptian Hieroglyphs</family>
      <!-- 苏美尔楔形文字:𒆠𒂗𒂠 -->
      <family>Noto Sans Cuneiform</family>
      <!-- 中日韩统一表意文字扩展 C:𫚥 -->
      <family>HanaMinB</family>
      <!-- 拉让文:ꥃ -->
      <family>Noto Sans Rejang</family>
      <!-- 越南傣文:ꪀꪑ -->
      <family>Noto Sans Tai Viet</family>
      <!-- 切罗基文:ꮳꮧꮢ ᨣ -->
      <family>Noto Sans Cherokee</family>
      <!-- 老傣仂文:ᨣ -->
      <family>Noto Sans Tai Tham</family>
      <!-- 安纳托利亚象形文字:𔘓 -->
      <family>Noto Sans Anatolian Hieroglyphs</family>
      <!-- 马姆穆文补充:𖤍  -->
      <family>Noto Sans Bamum</family>
      <!-- 图标字体(PUA): -->
      <family>OperatorMonoSSmLig Nerd Font</family>
      <!-- 巴塔克文:ᯤ -->
      <family>Noto Sans Batak</family>
      <!-- 古北欧文:ᛋᛖᚱᚣᚨᛚᚳᚨᚾᛞᛚᛖ -->
      <family>Noto Sans Runic</family>
    </prefer>
  </alias>
</fontconfig>

这个配置的意思是,把这些字体的优先级提高一些。当使用 fontconfig 的程序要显示字符的时候,它会指定一个模式,匹配到一个字体列表。渲染文字的时候,就可以遍历这个列表,直到找到可以显示这个字符的字体,所以一般来说,只要系统上装了对应字符的字体,它就能显示出来。

但是 Qt 额外地需要这个配置,因为 Qt 只会检查列表中的前255项。而世界上的不同文字那么多,所以想要能够显示它们,就得有一堆字体。比如 noto-fonts 这个包里就有614个字体文件,远超 Qt 支持的数量。总有些奇奇怪怪的文字被网友用来当颜文字,或者挂在名字上彰显个性。不这么调整一下,Qt 遇到了就只能「吃豆腐」了。

空心豆腐

当一个字符显示不出来的时候,那么怎么显示好呢?一般会显示成某种方框。Pango火狐会将该字符的 Unicode 码点以十六进制的形式显示在方框里边,这样虽然不知道这个字符长什么样子,但至少知道它是哪个字符,也知道多块豆腐是不是同一字符,在不能复制字符本身的时候很有用。比如当它出现在求助者的截图里的时候,比如当它出现在不能复制的地方的时候。

然而 Qt 不这样做。管你什么字符,Qt 统一显示为空心方框。从视觉上完全无法知晓它到底是什么字符,要是复制不到的话,就别想弄明白你缺什么字体了。

PS: Matrix 客户端 fluffychat 的 Web 版,使用的是 Fluffy 图形界面库,即使在 Web 版,文字渲染依然完全是自己做的。不管浏览器的设置不管系统的设置,豆腐块是带叉号的方框,还不能选中,十分讨厌。

非 BMP 字符

所有使用 UTF-16 的平台(Java、JavaScript、Windows、Qt),外加 MySQL 容易遇到的一个问题:非 BMP 字符(也就是那些 U+FFFF 之后的字符)会被当作是两个字符处理。随着 emoji 的流行,大家应该都修了不少。然而,Qt 在展示非 BMP 字符的时候,你可以选中半个字符。如果不小心漏掉半个的话,复制出来的半个字符就会变成问号(还好不是 GBK 时代那样弄乱后续所有字符)。

font features

一些字体可以通过 fontconfig 设置 fontfeatures 属性来启用(或者禁用)一些特性,比如连字,带斜杠的 0,小型大写字母,居中的中文标点,等等。Pango 很早就支持了,火狐最近也支持了,但 Qt 那边依旧没啥动静。(感谢 Coelacanthus 的评论。)

by 依云 at March 08, 2022 08:45 AM

March 07, 2022

Alynx Zhou

我如何在 Emacs 里面处理缩进宽度和 Tab 宽度

Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to make indentations 4 (or even 2!) characters deep, and that is akin to trying to define the value of PI to be 3. -- Linux Kernel Coding Style

当然,我不是在任何情况下都同意上面那句话,虽然在写 C 的时候它是绝对的真理。我个人倾向于取其中一部分——任何试图把 Tab 宽度定义为 8 以外的行为都无异于把 PI 定义为 3。

写这篇文章是因为我意识到一个问题:Tab 宽度和缩进宽度是两个无关的变量。很多人简单地把缩进宽度理解为 Tab 宽度,不过这不能怪他们,因为大部分的编辑器都把这两者当作相同的东西。而当我重新开始用 Emacs,才发现这两个本来应该是不一样的。

为了防止迷惑,首先解释几个我说的名词:

  • Tab 宽度:字面意义上的一个 Tab 字符应该相当于几个空格的宽度,在 Emacs 里面是 tab-width 这个变量,我个人建议固定为 8。
  • 缩进宽度:当你的代码应该增加一个缩进级别的时候,应该向右缩进几个空格的宽度,在 Emacs 里面每个模式都有不同的变量控制,比如 c-indent-offset
  • 缩进级别:代码逻辑层次(或许可以简单理解为作用域)每增加一层,一般就应该增加一个缩进层级。
  • 使用 Tab 缩进:不是说只使用 Tab 缩进的意思。事实上如果你的代码可以只使用 Tab 缩进,那么 Tab 宽度为几都无所谓,因为一个缩进宽度就是一个 Tab,但实际上你不一定只使用 Tab 缩进,因为你可能还包含对齐的情况,比方说函数头参数太多,而你希望换行后的参数能够和左括号的下一个字符对齐,那么这个距离大概率无法被 Tab 宽度整除。因此使用 Tab 缩进的含义是“总缩进宽度能够用 Tab 宽度整除的部分用 Tab,余数的部分用空格”,也就是 Emacs 里面 (indent-tabs-mode 1) 的效果。
  • 使用空格缩进:和上面相反,意味着缩进部分完全不使用 Tab 字符,那么 Tab 宽度为几也无所谓。也就是 Emacs 里面 (indent-tabs-mode -1) 的效果。

我个人的建议和倾向主要是两个,第一个是 Tab 宽度固定为 8,第二个则是如果你认为缩进宽度为 8 也就是一个 Tab 对你来说太宽,那么你就应该完全不用 Tab 缩进,也就是使用空格缩进,而不是修改 Tab 宽度。

这样做的理由很简单,主要是我最近修改 GTK 代码时候发现的,GTK 代码是典型的 GNU 风格,我一开始以为它是完全不使用 Tab 缩进,并且缩进宽度是 2,并且我在 Atom 里面也是把这个项目的 Tab 宽度设为 2,但我前段时间发现 GTK 实际上是使用 Tab 的,比如一个很有意思的情况:某段代码的总缩进级别是 5,而它实际上使用的是 1 个 Tab 和 2 个空格,如果你把 Tab 宽度设置成 2,那这段代码在你的编辑器里就会错误的显示成 2 个缩进级别,只有你的 Tab 宽度是 8 的时候才能正确的显示。

这些奇怪缩进的项目给了我两个教训:Tab 宽度和缩进宽度并不是一个东西,以及最好假设 Tab 宽度为 8。当然,以上的建议都是以遵照现有代码的风格为前提,应该不会有人任性到把别人的项目都改成自己风格再提交贡献吧。

我倒不是完全的 Tab 缩进党,比如在 Python 里我设置缩进宽度是 4,而在 JavaScript 里我设置缩进宽度是 2,主要是因为对于 C 这种不允许嵌套函数并且没有类似 class 这种层次的语言来说,缩进级别完全就是函数内部逻辑,当你总缩进宽度达到 24 的时候,你已经在函数里有三层逻辑了。而对 Python 或者 JS 这类函数经常是类的方法的语言,本身函数就已经带着一个缩进级别了,三个缩进级别仅仅只代表两层函数内部逻辑,更别提比如回调函数是匿名函数的情况了。

至于当缩进宽度不是 8 时使用空格缩进而不是用 Tab 然后修改 Tab 宽度的理由也很简单,和上面说的一样,完全使用 Tab 缩进时虽然 Tab 宽度不会影响总缩进级别,但是一旦遇到对齐的情况,Tab 宽度不一致,对齐的部分就不再一样了。

说了这么多理论,该到具体的我怎么在 Emacs 里面处理了,首先是默认值,我个人是定义 Tab 宽度为 8 并且设置允许使用 Tab 缩进。

(setq-default tab-width 8)
(setq-default indent-tabs-mode t)

默认的 C 编码风格是 GNU,但是 GNU 的编码风格实在是太恐怖了,特别是大部分人的入门书上应该都是 K&R 或者类似的风格。我换成了我喜欢的 linux 内核风格。有的变量你可以直接使用 setq,但另一些使用 setq 会说你声明了一个新的自由变量,这个时候还是遵照建议使用 Emacs 的 customize 系统吧。

(customize-set-variable 'c-default-style '((java-mode . "java")
                                           (awk-mode . "awk")
                                           (other . "linux")))

前面说过 Emacs 对于不同的模式使用不同的变量作为缩进宽度,这样对于编写配置其实很困难,因为让你时刻记住哪个模式用哪个变量显然不太现实,这里我用了一个比较投机取巧的办法——Emacs 有所谓 buffer-local 变量的设定,也就是说一个变量会有一个默认值,然后每个 buffer 都可以有一个该变量的副本,可以设置成不同的值,如果没有则使用默认值。利用这个功能我创建了一个单独的 buffer-local 变量 indent-offset,然后把以上所有这些都设置为该变量的别名,于是我对每个 buffer 只要修改 indent-offset 的副本就可以了。我这里只写了我使用到的模式的变量,如果有其他的就加到列表里,或者我看 doom-modeline 的代码里几乎包含了所有常见模式的变量,或许可以拿来用。

(defconst mode-indent-offsets '(c-basic-offset
                                js-indent-level
                                css-indent-offset
                                sgml-basic-offset
                                python-indent-offset
                                lua-indent-level
                                web-mode-code-indent-offset
                                web-mode-css-indent-offset
                                web-mode-markup-indent-offset
                                markdown-list-indent-width)
  "Different modes' indent variables to make alias to indent-offset.")

(dolist (mode-indent-offset mode-indent-offsets)
  (defvaralias mode-indent-offset 'indent-offset))

(defvar-local indent-offset tab-width)

那么接下来就是我们如何针对不同的 buffer 进行不同的设定了,这里分为两部分,一部分是这个 buffer 是否使用 Tab 缩进,另一部分则是缩进宽度设置为多少。Atom 里面有一个状态栏插件,点击就可以快速设置,Emacs 里面我写了两个简单的函数方便调用,执行对应的函数设置是否使用 Tab,并且它们都会询问你想把缩进宽度设置为多少。我分别把这两个函数绑定到 C-c i TABC-c i SPC。对于 GTK,操作起来可以是 M-x indent-tabs RET 2 RET

(defun indent-tabs (num)
  "Mark this buffer to indent with tabs and set indent offset to NUM chars."
  (interactive `(,(read-number "Indent offset (chars): " indent-offset)))
  (indent-tabs-mode 1)
  (when (/= indent-offset num)
    (setq indent-offset num)))
(global-set-key (kbd "C-c i TAB") 'indent-tabs)

(defun indent-spaces (num)
  "Mark this buffer to indent with spaces and set indent offset to NUM chars."
  (interactive `(,(read-number "Indent offset (chars): " indent-offset)))
  (indent-tabs-mode -1)
  (when (/= indent-offset num)
    (setq indent-offset num)))
(global-set-key (kbd "C-c i SPC") 'indent-spaces)

当然以防万一你真的遇到一个脾气古怪的作者,一定要使用 Tab 缩进并修改 Tab 宽度,我也写了个修改 Tab 宽度的函数。这个我绑定到 C-c i w 了。

(defun set-tab-width (num)
  "Mark this buffer to set tab width to NUM chars."
  (interactive `(,(read-number "Tab width (chars): " tab-width)))
  (when (/= tab-width num)
    (setq tab-width num)))
(global-set-key (kbd "C-c i w") 'set-tab-width)

现在我们有了给每个 buffer 修改设定的办法了,但是通常你对某种语言有一个自己偏爱的风格,肯定希望以这个为默认值,所以我写了一些代码,给每个模式设置成我喜欢的默认风格。同样地如果你装了更多的模式,或者和我有不同的喜好,就修改这些列表好了。

这里使用 (set-tab-width 8) 是因为有些模式比如 markdown-mode 把 tab-width 定义为 4,按照前面说的,我觉得这是错的,同时上游为了保持向前兼容不好修改,于是这里简单地覆盖掉。

(defconst indent-tabs-modes '((prog-mode . 8)
                              ;; `markdown-mode` is not a `prog-mode`.
                              (markdown-mode . 8)
                              (gfm-mode . 8))
  "Modes that will use tabs to indent.")

(defconst indent-spaces-modes '((lisp-mode . 2)
                                (emacs-lisp-mode . 2)
                                (js-mode . 2)
                                (css-mode . 2)
                                (html-mode . 2)
                                (yaml-mode . 2)
                                (lua-mode . 3)
                                (python-mode . 4))
  "Modes that will use spaces to indent.")

(dolist (pair indent-tabs-modes)
  (add-hook (intern (concat (symbol-name (car pair)) "-hook"))
            `(lambda () (indent-tabs ,(cdr pair)) (set-tab-width 8))))

(dolist (pair indent-spaces-modes)
    (add-hook (intern (concat (symbol-name (car pair)) "-hook"))
              `(lambda () (indent-spaces ,(cdr pair)) (set-tab-width 8))))

有些 modeline 包含一个显示缩进信息的部分,比如 doom-modeline 显示 TAB 或者 SPC 表示使用 Tab 缩进或使用空格缩进,然后如果该模式有自己的缩进宽度变量就显示,没有就显示 Tab 宽度(我没仔细读,总之它对这俩不做显式区分)。而按照上文,我肯定是倾向显式区分这两个东西的,所以我们不用它的,而是自定义一段 modeline,第一部分显示 TABSPC,第二段显示 indent-offset,第三段显示 tab-width。我没太搞懂 Emacs 的 modeline constructor 的语法,我觉得我写对了但却没有,于是最后变成 :eval 一个 format 调用了。

(setq mode-line-misc-info '(:eval (format "%s %d %d"
                                          (if indent-tabs-mode "TAB" "SPC")
                                          indent-offset
                                          tab-width)))

最后我在网上抄了两个配置,一个是让它按回车时候不要自动缩进。另一个是修改默认的删除缩进的行为,默认当你在对着一个 Tab 按下退格键的时候,Emacs 把这个 Tab 变成缩进宽度数量的空格,然后删掉一个空格,这太诡异了,我就让它删掉一个字符好了。

(setq-default electric-indent-inhibit t)
(setq backward-delete-char-untabify-method nil)

这样当你遇到一个和自己习惯不一样的文件,基本只要看情况调用 indent-tabs 或者 indent-spaces 即可。我在 Atom 用的插件还有一个自动猜测文件是使用 Tab 还是几个空格作为缩进的功能,不过我看了一下代码,它并不能解决我之前说到的 GTK 的问题,也就是说它会猜成 SPC 2 2 而不是 TAB 2 8,我懒得自己想一个猜测算法,于是就还是靠自己判断了。

一个我比较想实现的功能是记录每个目录我用了什么设定,因为基本上每个项目都用一样的风格,这样就不用每次编辑文件都手动设置。我知道可以在每个目录创建一个文件记录 Emacs 的一些目录范围变量,但是问题是不是所有项目都想让你添加一个编辑器相关的文件,甚至你都不好修改 .gitignore 排除你的文件。我比较想把这个存储记录丢到 Emacs 的目录里,不过并不知道怎么实现,如果哪天我搞清楚了,就去写一个。

by Alynx Zhou (alynx.zhou@gmail.com) at March 07, 2022 01:11 AM

February 26, 2022

Alynx Zhou

Emacs 和 Monaco 字体和 Box-drawing Character

2016 年的我开始用 Atom 这种“modern”的编辑器,2022 年的我却又开始用回岁数比我都大的 GNU Emacs。切换的理由其实很简单,我曾经以为一直能追上最新版 Electron 的 VSCode 会成为第一个纯 Wayland 的代码编辑器——只要 Chromium 那边支持纯 Wayland 就好了嘛,然而直到 Emacs 那边的 pgtk 分支合并进主线(以防有读者不太清楚来龙去脉我解释一下,Emacs 虽然有图形界面,但实际上只是用 X 实现了一个 Terminal 层,而传统的 GTK3 界面只是使用 GTK3 创建一个 X 窗口,然后其它操作都是通过 X 进行,这实际上非常不适合 GTK3,导致了很多 bug,同时也使 Emacs 没法利用 GTK 的 Wayland 后端。而 pgtk 分支则是在 X 部分之外另起炉灶,利用 GTK 实现了一个和 X 部分平行的 Terminal 层,全部的绘制操作都是以 GTK/Cairo 的现代程序方式进行,自然也就摆脱了对 X 的依赖。总之在 Emacs 这样又老又庞大的代码库上做如此大范围的工程我觉得可以称得上是一项壮举了。),Chromium 的 ozone backend 还是问题多多。虽然 Emacs/Vim 这种软件看起来确实有点老派作风,但没想到也有走在这些“现代”编辑器前面的地方。

至于这和我换掉 Atom 有什么联系呢?主要是我发现在家里的台式机上,所有 XWayland 程序在 nvidia 驱动下面都会有闪回的情况,也就是说你打字的时候突然会闪回前几帧的画面,过一会再闪回来,你经常看不到自己输入的字符。Electron 程序尤其严重,也就导致我没有办法使用 Atom 写代码,于是不得不捡起以前东拼西凑的 Emacs 配置重新研究。(奇怪的是我自己的台式机也是 nvidia 驱动,没遇到过这种问题。)

扯远了,这篇文章主要想记录的问题是什么呢?其实还要和 Emacs 绘制界面的方式有关系,对于 Atom/VSCode 这种基于浏览器的程序来说绘制点什么图形元素很简单,但是对于 Emacs/Vim 这种来自于终端里的程序,开发者们习惯的是处理字符而不是处理图形,于是你会发现比如 80 column ruler 或者 indent guide 这种东西,在 Emacs 里面其实是通过在对应的位置插入竖线字符实现的……我个人不太喜欢这样,一个原因是我以为竖线字符并不是占满整行而是上下有空白。我一直以为这是 Emacs 的问题——你干嘛用竖线字符画 UI 啊。

有问题的样子

直到有一天我输错了 alias 在 GNOME Terminal 里面打开了 Emacs,我惊讶的发现竖线竟然接上了头!

终端里的样子

我当时就震惊了,我的终端和 Emacs 用的是同样的 Monaco 字体,怎么会不一样呢?难道是 Monaco 字体有问题?于是我上网搜了一下,我以前一直以为是 Emacs 的哪个设置比如 line-spacing 我没搞好,怎么搜也搜不出来,这次换成搜字体一下子就找到原因了:一个 Alacritty 的 issue 里面和我有同样的问题,不过他是 tmux 的分割线接不上头,都是 Monaco 字体。

为什么只有 Monaco 接不上头呢?原来在字符界面下画这些竖线的字符和平时用 Shift+\ 输入的字符并不是一个,这类字符叫做 Box-drawing Character,主要的范围是 U+2500U+257F,这些字符用于在终端里绘制方框或者其它形状,所以应该是没有 padding 的,才能接上头,而 Monaco 这里有问题,它给这些字符加上了 padding,导致接不上。我尝试着给 Emacs 的字体换成 Source Code Pro,竖线立刻就连上了。

怎么解决?换字体?不可能的,我是 Monaco 的狂粉,看惯了 Monaco 再看别的字体都觉得傻了吧唧的。如果不是因为它好看我才不会忍受它这么多缺点(没有内置粗体,虽然是等宽字体内部的连字表却和非等宽字体一样,有版权不能二次分发)。解决方法其实比较简单,把 U+2500U+257F 的字符换成正常字体里的就可以了。简单的解决办法是用 FontForge 的同个实例打开 Monaco 和另一款字体(我选择了 Menlo,Menlo 是苹果用来替代 Monaco 做内置默认等宽字体的,应该会比较接近),然后选择 Monaco 的这个范围清空,然后选择 Menlo 的复制粘贴过来。不过我没在 FontForge 里面找到连续区间选择的办法,上网搜了一下说可以用它的脚本 API 选,办法是打开 File 菜单里面的 Execute Script,执行 fontforge.activeFont().selection.select(("ranges", None), 0x2500, 0x257F)。如果你不知道怎么把 Menlo 里面的一段字形复制粘贴到 Monaco 里面,你也可以在 Menlo 里执行这段脚本,然后反选,全部清空,然后把这部分生成一个字体,再去 Monaco 里面选 Elements 菜单里面的 Merge Fonts。

或者你也可以看看这个 叫 Menloco 的项目,是我偶然间搜索到的有同样问题的用户的解决方案,这个项目包含更多的细微 tweak 脚本,帮你利用 Monaco 和 Menlo 合并出一个 Box-drawing Character 能完美接头的字体。不过有几个地方需要注意,一个是这个项目的作者应该是 macOS 的用户,如果你不是 macOS,需要自己想办法搞到 Menlo.ttf 和 Monaco.ttf,简单的办法是找个用 Mac 的朋友让他发给你,不过有可能你得到的是 Menlo.ttc,需要用 FontForge 打开选择 Regular 字重,然后导出成单个的 ttf。你还需要修改 utils/find-font.sh,这个脚本的 font_paths 只包含 macOS 放置字体的目录,你得加上你自己放这两个字体的目录。以及这个项目默认生成的字体名(不是文件名)叫 Menloco,如果你不想修改已有的写着 Monaco 的配置文件的话,就把 merge 这一项下面的 --font-name=$(RESULT) 改成 --font-name=$(INTO) 就好了。

生成一个没问题的字体之后你还可以像我一样用 FontForge 做一些修改,比如说我发现 Menlo 有很多 Monaco 没有的字符,于是我直接把 Menlo merge 进了生成的字体里。并且我之前提到过 Monaco 作为一个等宽字体,内置的连字表竟然是非等宽的,不是像 Fira Code 那种把不同的编程符号连字起来同时保持等宽的连字,而是像普通无衬线一样把 fi 一类的字符连起来变成单个字符宽度。我被坑得最狠的一次就是 review 同事的 patch,我问他这里是不是少了个空格,他说在他那看没问题,最后我发现是 Monaco 连字了!虽然你可以通过配置 fontconfig 关闭连字,但是 Firefox 是不吃这个配置的,而你也不可能给每个网页的代码块都加上关闭连字的 CSS。所以我直接在 FontForge 里面干掉了连字表,具体方法就是打开 Element 菜单下面的 Font Info,点击左侧的 Lookup,选中带 liga 的项 delete 之后导出字体即可。

还有一个比较古怪的 Emacs 问题,Emacs 设置字体和其它程序不太一样,可以先设置一个默认字体然后针对不同的字符集设置不同的字体,一般要为中文单独设置字体才能得到合适的效果,就像下面这样:

(set-face-attribute 'default nil
                    :family "Monaco"
                    ;; :slant 'normal
                    :width 'normal
                    :weight 'normal
                    ;; 1 height is 1/10 pt.
                    :height 140)

(dolist (charset '(kana han symbol cjk-misc bopomofo))
  (set-fontset-font t charset (font-spec :family "Noto Sans Mono CJK SC"
                                         ;; :slant 'normal
                                         :width 'normal
                                         :weight 'normal)))

首先要注意 height 的单位是 1/10,所以你想要的字号需要乘 10 才行。

然后你会发现明明你只设置了一个字号,可是中文和英文字体却不是等高的!也就是说如果你在本来都是英文的一行里面输入一个中文字,那这行的高度就会突然跳一下变高,非常烦人,也许是这两个字体在同样的字号的时候尺寸并不完全一致,但是明明其它程序都能正常处理,为什么这里这么怪!

你可能会想要对中文那段单独设置 size 缩小一点,但是这样不行,你用 C-x C-= 放大字体的时候中文字体就会固定大小不跟着你变了。正确的解决方法是加入下面一句:

(setq face-font-rescale-alist '(("Noto Sans Mono CJK SC" . 0.85)))

这里你可以对任意的字体指定缩放参数,不会影响按键放大缩小。我尝试了一下 0.85 比较合适,虽然可能这样汉字看起来会稍微小一点,但是 0.9 就太高了仍然会跳。或许你会问那这样中文字宽不是英文字宽两倍了?那没有办法,Monaco 本身就属于一个比较宽的字体,那些满足英文宽度是高度一半的字体都比较瘦长,我个人是不喜欢这样的风格的,所以对于我来说等高就行了,宽度我不太在乎。

更新(2022-02-25T19:01:31):我最近研究了一下,发现原来字号并不等于行高。虽然字体有一个 em size 作为基础的方块大小,但是设计师经常指定一些奇怪的 ascender 和 descender 值让字体的高度超出 em size……我不太清楚 Atom 或者说 Chromium 是怎么进行中英文混排的,不过 Emacs 排版时候是对齐 baseline,然后 ascender 和 descender 完全一致才能保证行高不会在切换字体时候变化。但是这实在是太难了,大部分中文字体和英文字体都对不上,特别是 Noto Sans CJK 系列,不知道为什么比其它的高特别多。一个简单的解决办法是使用等距更纱黑体,里面混合的英文等宽字体 Iosevka 和中文的思源黑体拥有一致的 ascender 和 descender,但我实在是不喜欢 Iosevka。并且不知道为什么,Noto Sans CJK 和等距更纱黑体里面的内置的思源黑体应该是同一种字体,Noto Sans CJK 就要比他高很多。我还尝试了修改 Monaco 的 ascender 和 descender,不过这部分非常复杂,涉及到好几个不同的值,而且不同字体比例尺也不一样。最关键的是修改之后相当于把字体拉高了,于是 box-drawing character 又接不上了……上面那个修改缩放参数其实也不是无级缩放,实际上是乘字号之后取整然后再去找对应大小的字符,所以其实就是找小几号的 Noto Sans CJK。具体的可以在 Emacs 里面 M-x describe-font 查看详情。

更新(2022-02-26T09:51:25):补充一下,浏览器的混排应该和 Emacs 是一样的,我刚才尝试了一下,我的博客行高固定是因为我给 <pre> 设置了 line-height: 1.5,这个大小超过了 Noto Sans CJK 的行高,如果删掉这一行,你就会发现含有 Noto Sans CJK 的行比只有 Monaco 的行要高很多。不过 Emacs 没办法像浏览器一样指定一个最小行高,只能是它自己根据这一行的字符计算行高,所以没什么比较好的解决办法。

搞定这些之后,至少 Emacs 里面看起来比较顺眼了。

正常的样子

by Alynx Zhou (alynx.zhou@gmail.com) at February 26, 2022 01:51 AM

February 17, 2022

Lainme

解决Snapcraft的方框字问题

从snap安装的软件经常会出现文字显示为方框的情况,目前发现一个比较有效的解决方式如下:

先执行

snap run --shell [软件名]

然后执行

fc-cache -r

运行结束后退出即可。

by lainme (lainme@undisclosed.example.com) at February 17, 2022 02:57 PM

February 15, 2022

Alynx Zhou

不写文章的博客生成速度最快

春节假期虽然是假期,但是在家的时间里我基本没闲着,毕竟按照传统(指 Ken Thompson 在假期里写出了 UNIX 的第一个雏形的传统,不过维基百科上并没有详述是不是这样,我也懒得考证),假期是造轮子的好机会。所以我就胡乱捣鼓一通看看能不能让我的博客生成器更圆一点。

重新理解 await

第一个简单的修改就是把启动时加载文件的部分并行化,其实我也没有非要尽可能并行,性能也不是我第一位的追求。

我一开始写代码的时候知道有 await 之后就开始在各种地方给 Promise 用 await,随便你怎么说都行,后来我也才意识到,给所有的 Promise 都加上 await 不就是相当于每一个 Promise 都写在上一个的 then 里面,那不还是顺序执行吗?写多了以后对异步有理解了我才想起来这里完全可以用 Promise.all(),反正加载插件/脚本/模板/语言是不太有先后依赖顺序的(非要说的话,插件和脚本里可能包含模板引擎,所以可能下一步是给前两个和后两个分开顺序加载)。

程序解决不了的事情就让人来做

随便你怎么说,我一直觉得有些程序很难理解的事情而人类很好理解的就该让人类来做,而不是费很大力气得到不好的效果。一个多少接近这一条的问题就是主题文件的依赖问题——很多生成器比如 Hexo 在以 server 模式运行的时候都有 watch 功能,可以监测文件变化然后实时重新生成,用户只要一刷新就可以看到最新的更改。直接看上去不难,写 Node 的谁还不知道 chokidar 了。但是问题就在于,普通用户写文章不太需要一边看渲染一边写(你不会连 Markdown 这么简单的格式都不能脑补吧?我鄙视 Typora),反而是主题作者经常需要看到自己刚编写的样式是什么样子,而这是一个很复杂的地方,CSS 预处理器和 HTML 模板引擎通常都有 import/include 一类的语法,让你不用写很多重复的片段,而博客生成器并不能理解每一种预处理器和模板引擎的语法。假如你在 A 模板里 include 了 B,然后修改了 B,此时你刷新一个用了 A 模板的页面,你是看不到页面更新的,那在最需要这个功能的时候这个功能变成了废物。

有一个简单的解决方案是不要预编译你的模板,而是每次需要渲染页面的时候重新读取模板文件编译出一个函数来。不过这对于生成器而言不太优雅,有些模板引擎比如 nunjucks 可能有内建的 cache 支持,但另一些可能是没有的。再说你来来回回读这么多次磁盘,那干嘛不直接把渲染页面的工作也挪到用户浏览器(指前端)里呢?还要博客生成器做什么。

我一开始的打算是既然生成器自己不好理解依赖语法,那就像处理博客文章一样,让主题作者给文件加上 front matter 记录这个文件的依赖。不过问题又来了,对于直接使用的模板(指博客生成器读取并编译它们)可以在读取时去掉 front matter,但是假如这个模板 include 的模板也带有 front matter,此时实际上是模板引擎(比如 nunjucks)自己去磁盘上读那个文件,它不会去掉 front matter。而给每一种模板引擎都重写文件加载器显然是不现实的,所以最后的方案就是要求主题作者提供一个 file-dependencies.yaml 的文件,里面记录每个文件的依赖关系,大概像是这样:

layouts:
  includes/layout.njk:
    - includes/footer.njk
    - includes/header.njk
    - includes/sidebar.njk

第一层是需要 watch 的目录名,因为不是所有的目录都需要实时刷新,显然这种耗时的操作越少越好,第二层是依赖其他文件的文件,第三层则是一个被它依赖的文件的列表。程序里面我写了一个 Watcher 类,启动时会读取这个文件,然后将它翻转过来:以被其它文件依赖的文件作为 key,而依赖这个文件的文件成为 value 列表。因为我们总是检测到被依赖的文件变化之后才开始统计受到它影响的文件。然后就是一个递归收集受到影响的文件的函数,因为文件依赖并不是只有一层的。收集之后分别给这些文件也加入到需要更新的列表里面。

写完这个功能之后我最大的感叹就是:要是我写主题的时候能有这种功能,我应该能节省不少检查的时间吧……

后面闲着又给这个做了点修改,一个是支持用 glob pattern 作为被依赖的文件,虽然我个人觉得还是把需要的文件都列出来算了,但是想必对于处在开发调试期的主题来说很有用,作者可能不一定想得起来及时更新这个文件。另一个是考虑到使用 glob 之后很容易在无意间搞出循环依赖,又写了在递归里打破循环依赖的功能,我通常不擅长处理递归的逻辑,但我脑子灵光一闪想起来可以给函数最后一个参数设置成一个记录已经处理过哪些文件的 Set,然后递归的时候作为参数传进去,然后就这么成了。

现在如果开启 server 模式,各种文件以及语言文件模板文件,甚至包括这个依赖描述文件自己都是实时更新的了,只有主题和站点的两个关键配置文件需要重启之后才能重新加载。

削减依赖的路上没有最少只有更少

事实上我在添加依赖的时候已经非常克制了,我尽量只使用那些我不得不用的 npm 包,有些功能能自己实现的我都自己实现了。但是总有还能去掉的依赖。我首先干掉了 Stylus,理由很哭笑不得,它好像不是那么流行了,我都找不到合适的支持它的 Emacs 插件。我本来想换成 Less 或者 SCSS,但是前者语法和 Stylus 差得有点多,后者有好几种语言的实现,本来我看 GNOME 都在用 C 写的 libsass,我觉得挺好,也打算用这个,结果发现它教程里挂着说“不推荐使用 @import 而推荐 @use 但目前只有 dart 版本的实现支持了后者”,让我感到说不出来的难受,我不太喜欢这种奇怪的新语言,而且搞这么多实现,还整出参差不齐的语法,实在是麻烦,虽然 dart 可以编译到 JavaScript,我还是放弃了。其实我自己写 Stylus 的时候不是放飞自我型的,而是尽可能贴近 CSS 型的,所以干脆把我的样式都改成了原生 CSS 了,顺手就干掉了一个依赖,如果有需要的人还是装插件吧。

再之后就是我一直使用 glob 来匹配文件,但是它还依赖一些别的包,我在想能不能用依赖更少的包替换它。glob 使用 minimatch 分析 glob pattern,但是比如 chokidar 使用的是另一个替代品叫 picomatch。我找到一个叫 fdir 的库,虽然接口用起来怪怪的,但是如果你用它做 glob,只需要它和 picomatch,没有引入别的依赖,于是我就试了一下。但是不得不感叹作者能不能写点阳间的代码,这个库默认忽略软链接文件,这显然是不能用的,但是假如你设置解析链接文件,它倒好,就算我要的是相对路径,它还是直接解析并拼接了绝对路径——我只是想让你把链接文件当普通文件返回一下!就在我打算放弃并用回阳间的 glob 包的时候,我发现 chokidar 自己依赖的 readdirp 库也可以配合 picomatch 使用。但是如果你直接把 glob pattern 丢给它让它自己调用 picomatch 编译的话,又有一些阴间的限制,所幸可以给它传自定义的过滤器函数,正好我还需要支持一些别的功能,就自己编译 glob 传给它了。

实际上我现在只有 10 个左右的直接依赖,全部的运行依赖大概是 36 个,在 Node.js 编写的程序里面应该算是相当克制的了,考虑到这里面大部分都是 nunjucks 的间接依赖,我觉得我做得相当不错。

对于其它我编写的简单的 npm 包我还是比较追求 0 依赖,只有 0 依赖的包才是比较可控的,如果一个 npm 包在 npm 上显示有 1 个依赖,那你就不能确定这一个依赖有多少间接依赖,而不更新的间接依赖会引入多少安全问题你也无法得知。我在给 Hikaru 选依赖的时候一般也会倾向于 0 依赖的包(这就是为什么选择 commander.js 而不是 yargs 的原因)或者是一些虽然有依赖,但是递归翻几次就是 0 依赖的包(chokidar 属于这类,而且我还给 nunjucks 提过一个 PR 使用 commander.js 代替了 yargs),而不会选那些依赖摞依赖无穷无尽的包。

这里不得不吐槽一下 jsdoc、mocha 这些大型的开发用的工具库,依赖实在是太混乱了,甚至 jsdoc 似乎同时依赖 marked 和 markdown-it……开发者写新功能的时候能不能长点心啊。还有我之前用 react-scripts 的时候,用旧版提示依赖里面有的版本有安全问题,升级到新版依赖算了一大堆,甚至出现了不同版本的间接依赖,而且仍然是有问题的版本,还是希望开发者对自己的依赖都关心关心,及时更新依赖版本,不要锁死一个版本懒得改就觉得万事大吉了。

如果你对削减 npm 包的依赖感兴趣,强烈建议你读一下 chokidar 开发者写的这篇文章

worker_threads 是多线程,但和我想的不太一样……

我:你懂 Node 吗?为什么我多线程比单线程慢? 铁道迷:Node 就是单线程拖拉机啊。 我:你不懂,88。

凡是跟你说“Node.js 只有单线程”的人,都可以直接和他说“你不懂 Node.js”了。Node 要解决的就是非阻塞 IO 的问题,而非阻塞 IO 肯定不是多线程能解决的。至少对于内部的 fs 来说,调用异步函数的时候是 libuv 从线程池里面拉出一个线程去解决任务,而 JavaScript 本身的线程不会被阻塞。如果你还想了解“有哪些代码会阻塞主线程而哪些代码不会”,可以看 Node.js 官网的这篇文章

说实话,对于用户自己写的 JavaScript 代码段,似乎是没什么好办法把它丢到主线程之外的线程上去执行的……请务必注意“回调”,“异步”和“非阻塞”的区别——不是所有回调都是异步的,比如我传了一个回调函数它也可能是同步的,而就算你把一段耗时的同步代码套上 setImmediate 和 Promise,也不意味着它就不会阻塞主线程了……它执行的时候还是在主线程执行,只是你把它延迟了,推到了 event loop 空闲的时候去运行——但轮到它运行起来还是阻塞住了主线程。(说出来不怕笑话,我也是写得多了最近才认识到这些区别(乐),如果我写的不对还希望大家指正。)

起因是我读了 Sukka 的这篇文章,介绍了 Node 新加的 worker_threads 模块,是允许你开启子线程运行代码的。正好我想到我的博客生成器里有一段分析每篇文章内容并修饰里面的图片和链接的代码,要是能起一个线程池,把每个文章分配给不同的线程去处理,那多是一件美事啊!

我发现这个 worker_threads 并不是很好用,它并不像 pthread,直接运行一个函数,而是要求你必须传一个 JavaScript 脚本的路径进去,这就给代码编写搞出了很大的麻烦。我做的修改在另一个 worker 分支里面,可以访问 https://github.com/AlynxZhou/hikaru/commits/worker 看到。不过一个尴尬的事情是写好以后我跑了几次,发现多线程还没有我之前单线程运行快……而且很反直觉的是线程越多越慢,在我 12 核心的处理器上四个线程是跑在主线程之外最快的,比什么 2、6、8、12 都快。

说实话,我也没有搞清楚到底是怎么回事,可以确定任务确实是分发给了不同的线程,但是为什么会这么慢呢?一个可能的原因是在不同的线程之间并不是像 C 一样直接共享内存空间,传递参数的时候 Node 是复制之后传递的,虽然我觉得我传递的数据量不至于让复制造成瓶颈吧,但这是我能想到唯一的原因了。而且阅读例子的时候也发现,好像推荐的用法是比如做 http server,每一个线程都是启动之后一直监听,而不像我这样是不断的分配大量的小任务。总之我很需要一个像 pthread 那样简单的给我执行一段代码的线程,而 worker_threads 好像倾向长时间运行一个单独文件……强扭的瓜不甜,我还是放弃吧。

然后说回到性能这件事上,我倒不是觉得生成器现在的性能难以忍受,否则我就该用 Hugo,相反我实在是觉得现在的生成速度出乎意料的棒了,而且又不像 Hugo 只支持 go template,Node 有很多种模板引擎可以选(我就是不喜欢 go 你来打我呀)。完整生成一遍我的博客大概耗时 800 毫秒,而且我也没有把渲染啦解析啦之类的功能用多线程解决,并且我的生成器没有缓存,每次都是从头生成的。没有缓存这点其实和之前 watch 文件的功能很类似,不够可靠还不如不要算了。在我以前用 Hexo 的时候它是有缓存的,但是这个缓存经常导致奇怪的结果,比如该更新的页面没有伴随文章一起更新之类的,我也不是很理解到底怎么回事(特别是考虑到 Hexo 里面还有一个奇怪的 JSON 数据库 warehouse),所以每次我都是 clean 之后再从头生成,那我自己实现的时候自然就砍掉这种不靠谱的功能了。

当然我问了琪神 Hugo 生成他的博客耗时多少,似乎只需要 200 毫秒的样子,不过除了 go 是编译型语言而且 Hugo 肯定用了协程渲染以外,他只有 6 篇文章而我有 83 篇文章也是一个很重要的因素!他甚至连分页都不需要生成!

所以如果你真的只追求速度不追求别的,什么语言都是浮云,总结出来就一句话:不写文章的博客生成速度最快!

by Alynx Zhou (alynx.zhou@gmail.com) at February 15, 2022 10:33 AM

February 11, 2022

Alynx Zhou

StackHarbor 的 2021 尾记

今年一直拖着没写总结,不是因为懒,主要是因为我总觉得好像距离去年写总结也没太久,我还能回忆起来去年写总结时候是什么样……也说不上是因为我记忆力不太好还是因为这一年实在没什么能让我记住的事情。

2021 年工作没什么变化,不过我换了个好一点的住处,虽然仍然是旧楼但是里面是新装修过的,至少让人待着的时候感到舒适,而且还可以吸室友的猫,虽然小猫也经常搞破坏,不过猫做什么都可以原谅。

看了去年的总结,才发现我是 2021 年把 FlipClock 改成 Meson 管理编译的,于是想起来我这一年学了 Meson 怎么用,因为 CMake 实在是让我烧脑筋。我今年还终于让 scrcpy 能够模拟 USB 键盘,这样使用 scrcpy 的时候也能用数字键选词了,多少算是在一个大项目里面做了一个大贡献。

然后其他能记得住的就是最近折腾的一些东西了,春节假期没写博客也是因为忙着写代码。写了一个简单的网页小游戏,基本是平面版神庙逃亡,感觉还是挺有成就感的。然后给之前的弹钢琴页面改成了原生 JS 实现,放弃 React 的原因是依赖太多了,而且不是每个软件包都能及时更新自己的依赖。比如你用一个老版本的 create-react-app 会带来许多有漏洞的依赖,然后你更新到最新版因为依赖变了,有些库又依赖了另一个有漏洞的版本。我觉得为了简单的 UI 和数据绑定这个代价是不值得的,所以我就重写了一个。然后给我的博客生成器添加了文件依赖的支持,很久很久以前当我刚开始用静态博客生成器的时候就在头疼这个,博客生成器理解不了你的模板引擎或者预处理器的导入和扩展语句,于是没办法在一个文件更新的时候更新所有包含它的依赖,导致主题开发要不停的关掉生成器再开。一开始我是想给这些文件都加上 front matter,但是我发现虽然我自己加载的时候可以去掉 front matter,模板引擎处理导入语句的时候是他自己读文件所以没办法去掉 front matter,我不想给每个模板引擎都写自定义的 loader,所以最后改成用一个单独的文件记录依赖了。最后的结果就是单独抽出了一个 Watcher 类,可以查表查出所有需要更新的文件然后调回调,效果还挺不错的。以及本来我想清理一下博客的样式然后顺便换个 CSS 预处理器,因为感觉 Stylus 不是特别火了,但是考察了一下,less 的语法和 Stylus 差得有点多,而 scss 虽然使用广泛,而且有纯 C 语言写的实现,但是它有好多个实现,并且官网上说只有 Dart 语言写的版本才不过时,其它的都只实现了过时的 @import 而用户应该用最新的 @use,总之搞得这么麻烦看起来就不想用。虽然 Dart 能编译成纯 JS,我还是觉得没兴趣。于是最后我把我的样式都改成了原生 CSS,然后去掉了生成器内置的 Stylus 支持。

显示器从去年的一个换成了两个,因为我是一个经常最大化窗口的人,切工作区虽然快,但是比如在浏览器和代码编辑器之间切换的时候还是会打断思路,有了双屏就没这个烦恼。一开始我想的是双屏更方便一边全屏游戏一边干别的事情,不过好像也就那样,因为从全屏切出来到另一个屏幕也不是很顺畅。不过买双屏的时候很头疼的就是因为不是一个批次,两个显示器的色温差得不是一点,最终是换了一台然后用校色仪调了好久,虽然不是 100% 一致,但是至少差不多了。

说到这个我想说,感觉幸运和倒霉是伴随而来的,不太可能一直幸运,经常是发生一些开心的事之后又发生一些不开心的事,比如去年买了新相机,经常没事拍点照片什么的,结果昨天晚上我突然发现不开镜头盖的时候 CMOS 有个红点……我觉得我基本可以排除激光损伤,应该就是坏点,但是我用机内的像素映射却解决不掉。感觉最后还得跑一趟维修站,而我在老家,一时半会不会回北京,这边也没有维修站,总之麻烦的事情就会让我心累。买了一张升降桌,不过这个最便宜的款式不太靠谱,现在升降功能不太正常,于是只是变成了一台大桌子。不过椅子坏了之后换了个比较便宜的工学椅倒确实令我满意。

今年没有换手机,主要是把这个钱投资到镜头或者灯上让我更高兴一点,手机厂商出的手机真是越来越烂了。不过把备用的 iPhone 8 换成了 iPhone XR,在二手频道看到一个成色很好,而且是红色款的,我早就觉得 iPhone 8 屏幕太小了。

动漫也想不起来看过什么,今年也许只看了 EVA 最后一部剧场版和 love live superstar,反正看 EVA 就是大家都想看看他怎么收尾而已,总体来说还是不错,特别是看了纪录片之后。然后 love live 我以前没看过,我的评价就是挺好听的。然后我感觉在电影院看到 Fate/Stay Night HF 第三季的可能性应该是没了,特别是考虑到今年院线上的国外电影的比例……还能说什么呢懂得都懂哈哈。

想不起来看过多少书,总之前段时间有天晚上睡不着,一口气把挪威的森林看完了,我早就买了这本书,但是以前都是打开看几页就看不下去了,这次一口气看完感觉还挺好的。

今年的另一个好消息是 NVIDIA 驱动终于补全了 Wayland 支持,感谢所有在这个过程中努力的人,比如 NVIDIA 那个一直在处理相关事情的用蜗牛做头像的老哥,事情总要是靠人推进的嘛。不过回家之后莫名发现我的 Atom 在 XWayland 下面会疯狂地闪回,明明我自己住处的电脑也是一样的配置就没问题。然后这时候我发现 Emacs 的纯 GTK 分支已经合并进了主线,也就是说 Emacs 现在终于是纯 Wayland 程序了,我立刻搞了一个最新版本,然后捡起来以前的配置文件,又仔细研究仔细鼓捣,目前多少是堪用了,虽然还有不少地方不像 Atom 那么习惯,主要还是我懒得仔细研究,反正凑合用,我是真的怀念 Atom 上的一些功能,比如注释反注释代码块、移动代码块、方便的查找替换还有现代化的界面……Emacs 的界面元素感觉还是基于文本的,然后包括粗体字渲染感觉也不太光滑,比如 indent guide 或者 80 column ruler 这种东西就应该直接在界面上划线嘛,不要用字符做。不过我在用 Atom 的时候也怀念 Emacs 的一些功能比如纯键盘分屏和 C-SPC Mark Region。

今年的游戏时间基本都拿来打 Dota 了,虽然看 TI 看得很难受,但是和朋友一起玩游戏还是挺快乐的,这也属于幸运和倒霉的周期了吧。塞尔达什么的,等我想起来慢慢玩……

跨年的时候还是和去年一样朋友聚餐了,我还在 B 站上传了 vlog,和蓝猫铁道迷一起吃寿喜锅很开心。今年写代码没少让琪神当小黄鸭,谢谢琪神。

总之先写这些,如果我又想起什么,我后面再慢慢加上吧,最近冻着了嗓子疼,希望倒霉的周期赶紧过去,过段时间还得找找机会去修相机呢……

by Alynx Zhou (alynx.zhou@gmail.com) at February 11, 2022 01:18 AM

February 03, 2022

中文社区新闻

调试符号(debug)包和 debuginfod

我们很高兴地宣布 Arch Linux 将有调试符号包了。
我们的 debuginfod 实例将提供调试符号信息和源码列表,这些可以被调试器比如 gdb 和 delve 利用。
https://debuginfod.archlinux.org/
一些由赞助商提供服务器的镜像站已经开始提供 debug 软件源的镜像,同时我们正在商讨新的镜像站要求

目前并不是所有包都提供调试符号包,这是我们正在进行的工作。
更多信息请参阅 Debuginfod 维基页,以及我们近期刚更新的 Debugging/Getting traces 维基页。

by farseerfc at February 03, 2022 04:33 AM

February 02, 2022

百合仙子

Wayfire 迁移进展(四):不那么 high 的 DPI

本文来自依云's Blog,转载请注明。

使用24寸4k屏幕作为主屏的时候很简单,设置 scale 为 2 就好了。但是,当 2 嫌太大、1 嫌太小的时候,问题就来了。比如我希望使用 120dpi,把 scale 设置为 1.25 可好?

scale=1.25 text

而这才是理想的效果:

120dpi text

看不出来差别?放大八倍,你看差别多明显:

8x compare

正常 120dpi 渲染出来的文字边缘清晰犀利,次像素平滑左红右蓝。再看看 scale=1.25 的文字,线条经常糊掉,次像素平滑效果几乎完全被抹掉。实际看上去的效果就是跟透明麿沙玻璃看屏幕似的,线条边缘总是有点糊糊的感觉,1080p 的屏幕被降级成了 720p 似的。

之所以出现这样的情况,是因为 Wayland 只支持整数倍缩放。因为,Wayland 混成器不能告诉客户端你得把窗口给画成 1.25 倍的,而客户端也无法告诉混成器我这个图像画的是 1.25 倍。所以,混成器只好告诉客户端你给我画个 2 倍的图像吧。混成器拿到图像之后再缩小 0.625 倍,自然有些逻辑像素就不能对应到单个的物理像素上去了。

所以,我还是设置 scale=1,不要混成器帮我去缩放。我自己通过另外的办法告诉客户端把字写大点儿。图标之类的就顾不上啦,反而大点小点都还能看。比如我要 1.25 倍大小的文字,就这样做:

  • GTK 3:在 dconf 里设置org.gnome.desktop.interface.text-scaling-factor=1.25就好了。最开始的截图就是 dconf-editor 里这一项配置。
  • Qt:设置环境变量 QT_WAYLAND_FORCE_DPI=120
  • Telegram:除了上边这个环境变量外,额外地在它自己的设置里设置 150% 的缩放(Telegram 的字偏小所以要设置得大一些)。设置环境变量是为了 fcitx5。
  • waybar:config 文件中设置 heightstyle.css 中设置 font-size
  • Xwayland:和 X11 下的 HiDPI 设置差不多的。比如 GTK 2 设置 Xresources Xft.dpi: 120 就好了。

我遇到的差不多就这些了。没办法,Linux 就是这么乱 QAQ。不过虽然 Wayland 协议不支持,好歹还有绕过的办法。

by 依云 at February 02, 2022 09:23 AM

January 25, 2022

ヨイツの賢狼ホロ

咱和()的 2021 年

有些事情在慢慢变化的嘛,例如咱逐渐开始偷懒到只在 Matters 发表文章这件事。 以及换服务器玩脱了把咱自己的 Mastodon 搞爆炸了什么的……

可以去那里看看咱这一年写了些什么,和发了什么牢骚 。(笑)

以及人老了就喜欢偷懒啦,所以后面大概主要会用那边的 WordPress 来写文章了。

在这里,名字还是一样的。

至于这里嘛,虽然后面可能不会再有大的更新了,不过咱应该还会尽力留着。 万一哪天咱又有心情开始折腾了呢……

咱也谢谢和咱一起路过这六年的大伙儿了。

by ホロ at January 25, 2022 04:00 PM

January 22, 2022

中文社区新闻

linux-firmware 20220119.0c6a7b3-2 需要内核 >=5.3 以及做了拆包

linux-firmware 从 20220119.0c6a7b3-2 开始实现了内核固件压缩。 Linux 内核从 5.3 开始支持加载 xz 压缩过的固件。这需要启用
CONFIG_FW_LOADER_COMPRESS 内核编译选项。所有官方 Arch Linux 内核早已开启了这一选项[1]。

并且 linux-firmware 包已经被拆分成多个小包,进一步减少磁盘占用。拆包将一些很少使用的硬件上较大的固件文件分到了独立的包中。受影响的硬件包括: Mellanox Spectrum 交换机, Marvell 设备, Qualcomm 芯片集, Cavium LiquidIO 服务器适配器, QLogic 设备, Broadcom NetXtreme II 10Gb 以太网适配器。
如果需要的话请确保安装这些附加固件包。 [2]

[1] FS#72899
[2] FS#72559 + svn commit

by farseerfc at January 22, 2022 12:54 AM

December 31, 2021

Leo Shen

2021 Recap: Seek

And here goes 2021. Here's a snippet. What I Wrote In 2021 I wrote 3 English articles and 2 Chinese articles. I actually had a lot of blog ideas during this year, but the vast majority of them failed to become a full blog post. It's so hard to write something that is interesting and unique at the same time. Maybe I should lower my standard a little bit.

December 31, 2021 11:59 AM

December 27, 2021

frantic1048

Pop Up Parade - 真红(Rozen Maiden)

Shinku

看到这款真红的消息时十分激动,竟然能看到早年看的作品里喜欢的角色出手办,宣传图看起来也没啥问题,于是就订了。

拍照的时候想着搞个暗色的环境更搭一点,换上了纯黑色的背景纸,但是没想到反光太强了,拍出来变成了灰色,Darktable 里稍微调了一下也是深灰色。然后尝试灯亮度下来一点,结果噪点过多又拍了第二波。折腾一番之后发现深灰色也挺好的。

头一次遇到不是用纸质而是全透明的包装,还蛮好看的。

Shinku

本体没有需要组装的部件,直接放底座上就好了。巨长的双马尾保护得很好,没有出现悲剧,可喜可贺。

Shinku Shinku Shinku Shinku Shinku Shinku Shinku Shinku Shinku Shinku

Shinku Shinku Shinku Shinku Shinku

比较奇妙的是一些角度看过去,真红的微笑就消失了,特别是稍微偏低一点的角度。

Shinku

角度抬高,微笑又回来了。相机还是抬高一点拍好。

Shinku Shinku Shinku Shinku Shinku

极限距离!

Shinku

December 27, 2021 05:07 PM

中文社区新闻

libxml2>=2.9.12-6 更新需要手动干预

libxml2 包在版本 2.9.12-6 之前缺失了预编译好的 python 模块。这个问题已经在 2.9.12-6 中修复,所以更新时需要覆盖未被跟踪到的 pyc 文件。如果你在升级时遇到如下报错:

libxml2: /usr/lib/python3.10/site-packages/__pycache__/drv_libxml2.cpython-310.opt-1.pyc exists in filesystem
libxml2: /usr/lib/python3.10/site-packages/__pycache__/drv_libxml2.cpython-310.pyc exists in filesystem
libxml2: /usr/lib/python3.10/site-packages/__pycache__/libxml2.cpython-310.opt-1.pyc exists in filesystem
libxml2: /usr/lib/python3.10/site-packages/__pycache__/libxml2.cpython-310.pyc exists in filesystem

更新时请使用命令:

pacman -Syu --overwrite /usr/lib/python3.10/site-packages/__pycache__/\*

完成升级。

by farseerfc at December 27, 2021 08:18 AM

December 06, 2021

frantic1048

F:NEX - 伊蕾娜

Elaina

有一位乘着扫帚飞在空中,头戴巨大三角帽,灰色秀发随风飘逸,令某位路过的普通点兔爱好者都被吸引住目光,在智乃生日的日子掏出相机疯狂拍照的魔女,究竟是谁呢?

没错,就是她!

首先来个包装盒。

Elaina

非常重要的呆毛,可惜和帽子不能兼得。

Elaina

没有帽子的情况下,手势显得比较奇妙,像是在和另一个人比心,或是在说一张照片要卖一百亿金币的事情。(゚ ∀。)

Elaina Elaina Elaina Elaina

戴上帽子手势就科学了!

Elaina Elaina Elaina Elaina Elaina Elaina Elaina

Elaina Elaina Elaina Elaina

Elaina Elaina Elaina

December 06, 2021 06:23 PM

December 04, 2021

百合仙子

Wayfire 迁移进展(三):taskmaid, waybar 以及 mako 等

本文来自依云's Blog,转载请注明。

我又来更新我的 Wayfire 迁移进展啦~

我写了一个 taskmaid 工具,使用 wlr foreign toplevel management 扩展来提供窗口管理相关功能。程序自己作为 daemon 随 wayfire 启动运行,通过 D-Bus 提供接口供别的程序使用。你问我为什么不直接每个需要的程序直接使用 Wayland 协议?因为用起来麻烦呀。Wayland 提供信息的方式是一组一组的事件,也并没有高层次的库,处理起来只能一堆回调怼上去,相当不顺手。

它最主要的功能就是在 waybar 上标题当前窗口的标题啦。顺便加上了中键关闭和显示 app-id 的功能。应用程序图标因为没有办法准确匹配(比如火狐 nightly 版本的 app-id 也是 firefox),所以没有做。

其次是获取当前活动窗口所在的显示器接口名称。我使用这个名称来判断我是不是位于 E-ink 屏幕上,并为终端、Vim、skim、mutt 等工具使用专门适配过的亮色主题。这个方案比之前在 X11 下使用当前鼠标坐标来判断要灵活一些。当然更灵活的方案是匹配显示器的名称啦,这个数据 Wayfire 的 Wayland 协议里是有提供的,有需要的话我再做。Wayland 并没有一个协议来获取当前鼠标或者键盘焦点所在的显示器信息,所以我只好用窗口管理协议来跟踪活动的窗口了。

顺便还做了个 lswin 工具,列出打开的窗口信息。工作区太多啦,我又喜欢最大化,有时候会有窗口忘了关。甚至偶尔我还会不小心把窗口最小化了,然后没有办法给恢复回来……如果以后还经常出现这种情况,我再给 taskmaid 加一个恢复最小化窗口的功能好了。

除了使用 taskmaid 显示标题之外,我还加了个显示 AQI 的小脚本。以及之前忘记加网速指示器了,现在也加回来了。不得不说 waybar 比 Awesome 的那个顶栏要好配置得多。不仅不限语言,而且不是非得用定时器,可以在信息变动的时候及时更新信息,没变动就不浪费资源获取重复的信息了。我的 waybar 配置都在这里

我给 Wayfire 发了一个 pull request,添加了最基础的快捷键禁制器的支持,可以在 spicy 等软件中屏蔽 Wayfire 自己的快捷键了,大大方便了我对 Wayfire 和 Sway 的测试。当然也可以用于 VNC 啦。不过并没有像 Sway 那样加选项、支持主动禁用快捷键等功能,短期内我也不太可能会去加这个。原本我是想着在 Wayfire 里再跑一个 Wayfire 或者 Sway 啥的,但考虑到配置方面的问题,还是拿虚拟机隔离了比较好。

最新的 Wayfire 版本已经支持切换到之前的工作区啦~

桌面通知程序 mako,之前遇到两个问题。一是通知经常是糊的,没有适配 HiDPI 屏幕,而鼠标指针则一直都是又糊又大。我给修了,虽然我其实有点不知道我是怎么修好的……总之是整理了一下代码,为了减少调试日志而减少了与 Wayland 混成器的通讯,也变得更高效了。会记住上次显示使用的缩放倍率,所以不会像之前那样一出来是糊的、下一刻才会变清晰了。在 Wayland 协议里,客户端可以获知有哪些显示器、大小和缩放倍率如何,但是客户端并不能提前知道自己将会显示在哪个显示器上(倒是能够指定显示在哪个显示器上),只有显示出来之后才知道,然后做调整后再更新一下……

另一个问题是,在 Wayfire 下 mako 在全屏时不会显示通知,会被盖住。设置 layer=overlay 之后倒是能在全屏时显示通知了,但是它也会在 swaylock 锁屏界面上显示……后来了解到 mako 有模式这么个特性,我就在锁屏的时候切换到专为锁屏设置的模式下,解锁之后再切回来。反正锁屏是一句命令,自己拿脚本包一下就好了。

最近新的桌面环境稳定下来了,倒是没有再遇到更多的 bug 了。Spicy 会在里边的虚拟机跑特效动画时经常报个「Gdk-Message: Error flushing display: 资源暂时不可用」错误然后退出,我给它用 try_until_success 包了一下倒是问题不大。我也发现了不光是 Wireshark,也有 GTK 程序弹出菜单会显示在错误的位置。

那么就酱~

by 依云 at December 04, 2021 07:52 AM

December 02, 2021

Phoenix Nemo

配置 Fail2ban 保护 Proxmox VE

各种情况下 Proxmox VE 的登陆界面需要暴露在公网的时候,需要使用 fail2ban 来保护它不被暴力破解。

创建 filter

文件 /etc/fail2ban/filter.d/proxmox.conf

1
2
3
[Definition]
failregex = pvedaemon\[.*authentication failure; rhost=<HOST> user=.* msg=.*
ignoreregex =

创建 jail

文件 /etc/fail2ban/jail.d/proxmox.conf

1
2
3
4
5
6
7
8
[proxmox]
enabled = true
port = https,http,8006
filter = proxmox
logpath = /var/log/daemon.log
maxretry = 3
# 1 hour
bantime = 3600

重启 fail2ban

1
~> systemctl restart fail2ban

然后检查是否配置生效

1
2
3
4
~> fail2ban-client status
Status
|- Number of jail: 2
`- Jail list: proxmox, sshd

各种情况下 Proxmox VE 的登陆界面需要暴露在公网的时候,需要使用 fail2ban 来保护它不被暴力破解。

December 02, 2021 01:44 PM

November 21, 2021

ヨイツの賢狼ホロ

怀旧手机游戏不正经研究 - 引子

为啥人(?)老啦就喜欢怀旧咧?

先回忆下咱的手游史

咱最早在手机上玩游戏的时间大概能追溯到十多年前,当时咱住在咱的某一个亲戚家里,作为一个熊孩子(x),偶尔也会摆弄摆弄他的手机。以及到了现在咱还记得那台手机,三星 SGH-i718+。

Samsung SGH-i718

刚才从网上搜索了下,大概就像这个样子。看到那个 Windows 键的话,~~老学校~~玩家应该已经知道了,这部手机搭载的是 Windows Mobile 系统。(以及搜索的时候不经意间发现网上说这是三星第一台国行 Windows Mobile 手机)

Bubble Breaker:Amazon.com:Appstore for Android

虽然内置的游戏只有一个戳泡泡游戏来着(Bubble Breaker?),但是咱不知道怎么发现的(也许是忘了吧)这个手机可以上网,还可以下载 JavaME 游戏来玩,于是接下来的事情就可想而知了……

唉,从五块钱 1M 或者 30M 走过来的表示说多了都是泪啊 😂

以及后来咱也从这位亲戚那里玩到了 Windows Phone 7/8 和 Android 手机,虽然经常是他自己都搞不懂怎么用来请教咱的样子……

后来咱搬去父母家了,虽然他们也忙着做生意经常不着家,于是为了联系方便,就给咱买了一部手机。 Nokia 6700 Slide。

Nokia 6700 slide

虽然那个时候的咱并不知道啥智能手机塞班系统什么的,但是它也能和咱最早玩到的那部手机一样可以安装游戏。

以及它竟然还支持 3G 网络,加上咱当时用的也是联通卡,于是流量和话费迅速消耗…

然后在不知道什么时候惨遭标准结局(丢了)…… 在挨了顿骂几个月以后,家里还是给买了新手机,还是诺基亚。

Nokia C7

就是 Nokia C7 啦,后来才发现咱直接跳过了 S60v5 整个时代。(虽然咱还是能发现有同学买了 S60v5 系统的手机来着,例如堪称街机的 5230 )

Nokia 5230

除了触屏这个显而易见的特性以外,C7 还有当时颇为珍贵的 Wi-Fi 功能。(以及后来才知道当时国内为了推广自有标准 WAPI 强行让国行手机阉割 Wi-Fi 这档事)于是找地方蹭网一度也是咱的活动之一,直到家里装了宽带和无线路由器。

虽然没有数字按键了,玩之前玩过的 Java 游戏有些不方便。但是咱靠它玩到了很多塞班 3 游戏啊,大部分的画面都比那些 Java 游戏好多了。(嗯,咱那时候完全没听说过 N-Gage……)

Asphalt 5 (2009 video game)

例如现在还有更新的狂野飙车(Asphalt)系列,最早是 Gameloft 在 2004 年发表在 NDS 和 N-Gage 的游戏,后来也移植到了 iOS 、Android 等新的手机平台。图片上是咱记忆里在 C7 上玩的 Asphalt 5 。

再后来的某一天呢,咱父母的手机坏了,于是就以再给咱买一台作为交换换走了咱这台 C7。当时 Android 也开始流行起来了,于是鬼使神差间咱买了三星 Galaxy Tab 2 7.0 。

Samsung Galaxy Tab 2 7.0 P3100

没错,一台能打电话的平板!😂 所以咱那个时候怎么想到买它的呢。

于是接下来就是延续到现在的折腾 Android 的故事了, ~~就和咱接下来要聊的话题没太大关系了……~~

至于 iOS 嘛,咱买的第一部 iPhone 是 iPhone SE,于是就和怀旧更没啥关系了。

好啦好啦,虽然老的 iOS 和 Android 游戏也有不少佳作啦,但是现在的新系统上面几乎都运行不了那些老游戏啦。

至于去淘一下老手机平板什么的, ~~经费有点不足……~~

那么接下来呢?

在看了好几个同样爱好怀旧游戏的爱好者的视频以后,咱也被一股不可名状的引力所吸引开始在网上收购一些老手机了。(以及在自己和亲戚那淘一淘货)

于是接下来咱有点兴趣的问题就是,咱想玩 Java ME/Symbian/N-Gage 游戏的话,用哪部手机比较合适?

当然啦,假如自己还有能用的手机的话,那肯定是用现成的最节省成本。于是问题就演变成了如果要淘一淘二手的话哪些型号性价比更高了。

以及有些平台已经有差不多的模拟器了(例如模拟 JavaME 的 J2ME Loader 和模拟 Symbian 的 EKA2L1 ),那么在手机上用模拟器是否也还行呢?假如汝不是特别介意手指搓玻璃的话。

以及带物理键盘的 Android 手机搭配模拟器会不会效果更好来着,例如上一篇文章里那台多亲 F21 Pro。其它的有按键的 Android 手机(像是三星的~~土豪专用~~心系天下系列和黑莓的最后几部键盘 Android 手机)应该也能类比。

~~其实还是没钱买~~

那么是不是还可以扩展到其它的老电玩平台呢……像是 3DS/PSV 或者 PS3/Wii 之类的。

这大概就是长期计划了……说来刚刚打开某软件搜索价格的时候发现咱之前两百块收的 PSTV 已经涨的飞起了,虽然并没有什么关系(x)

于是接下来就是等快递了。 ~~(连鸽子都能说的这么清新脱俗)~~

by Horo at November 21, 2021 04:00 PM

November 20, 2021

百合仙子

Wayfire 迁移进展(二):Xwayland HiDPI 以及 waybar

本文来自依云's Blog,转载请注明。

这几天完成了一个很重要的功能:我让 Xwayland 支持 HiDPI 了!

实际上让 Xwayland 支持 HiDPI 的补丁早就有了,但是我当时尝试的时候补丁并不能很好地应用,我手动修了修,不明不白地应用上之后,并没有能够正常使用。现象是,DPI 是对了,但是窗口大小会不断地缩为原来的四分之一(长宽都减半),全屏时也只占左上角的四分之一。

这几天我给 xorg-xwayland 打上了新版补丁,然后在一番理解之后,给 wlroots 写好了相应的补丁,现在 Xwayland 终于也可以看清晰了!

这两个补丁,xwayland 那边是通过一个 X 窗口属性来设置缩放倍数,然后 xwayland 会告诉混成器自己的窗口使用了对应的缩放倍数,这样混成器就不会当它不支持缩放、强行给拉伸一下了。当然还有输入坐标的转换之类的。缩放倍数为 2 时,X 客户端会看到之前两倍的显示大小,并且 X 使用的坐标是 Wayland 这边的两倍,所以 Wayland 的输入事件从 X 服务器传给 X 客户端的时候需要乘以 2。

混成器这边,需要了解客户端传过来的 X 坐标和 Wayland 坐标不再相同,需要进行相应的转换。没有进行转换的结果就是,客户端告诉混成器说自己是 1024x1024 的窗口大小,然后实际上创建出来是 512x512 的。混成器再告诉客户端你现在只有这么大,然后客户端说好,我调整一下。于是又变小了……

两个补丁打上之后,由于头文件有变化,混成器可能需要重新编译一遍。然后按 X 的方式设置 HiDPI,比如设置 Xft.dpi: 192 或者 winecfg 里设置 dpi 为 192。如果有运行于 Xwayland 的 GTK 3 程序,也要设置 GDK_SCALE=2 GDK_DPI_SCALE=0.5。然后执行以下命令设置 X 属性,让 Xwayland 做相应调整:

xprop -root -format _XWAYLAND_GLOBAL_OUTPUT_SCALE 32c -set _XWAYLAND_GLOBAL_OUTPUT_SCALE 2

接下来就能愉快地 wine 和 gimp 啦~

这几天做的另一个比较大的动作是配置好了 waybar。我也不知道这个组件叫什么好,很多地方都叫面板(panel),i3 / sway 那边直接叫 bar。它现在经常出现在屏幕顶部所以我也有时叫它顶栏。总之就是显示窗口信息、系统托盘和状态指示器啥的那一条。

我是从它自带的配置文件修改的,但是风格给完全改掉了。原本的风格是一块一块的彩色背景的字,我嫌太过显眼,经过一番调整之后,给改成了黑底上的彩色字,跟我原来的 Awesome 差不多,也和我显示器的黑边挺配的。指示器的放置也是差不多的。十分遗憾的是并没有合适的窗口列表小部件可用。它自带的那个 wlr/taskbar 会把所有工作区的窗口全部显示出来,很挤。而且不知道为什么,一旦加上它之后就最小要占用 34px 的高度,太占空间了,所以作罢。我打算以后自己实现一个,现在就拿正在播放的媒体凑一下吧。它现在长这样:

我的 waybar

左边就是使用 playerctl 做的媒体信息。左键可暂停播放,滚轮切歌。字的颜色是和窗口边框匹配的。

中边留着给窗口标题。

右边依次是:

  • idle 禁制器。点一下眼睛亮起来,禁用无活动时自动休眠。
  • CPU 和 load 信息。
  • CPU 温度。太高了会变红。
  • 内存使用率。
  • 电池信息。充满电又插着电源线,它就隐藏起来了。预期充电或者使用的时候图标会出现,并且可以看到剩余时间。如果在放电并且电量低,应该会变红并且闪烁。这设定跟我 Awesome 的那个一样,不知道等到什么时候才能用上,到时候才能看到效果了。
  • 音量。左键单击是切换静音,滚轮调节音量。图标会显示设备类型(我这里有内建、HDMI(实际上是走的 DP)、蓝牙三种)。麦克风静音的时候也会显示图标。
  • 系统托盘。它终于可以在多个屏幕上同时显示啦~
  • 时钟。

我最终还是用上了那个包名为「otf-font-awesome」的图标字体。

waybar 比 Awesome 的 bar 好配置多了。很方便使用外部程序来定义。也不一定要用定时器。程序可以一直跑着,一行一条状态更新,就不需要在「更新不及时」和「更新太频繁消耗资源、干扰调试」之间抉择了。

我把 wayfire、waybar 以及其它一些东西的配置文件上传到 GitHub dotconfig 仓库了。XDG 标准路径挺好的。

还有一些小的更新——

壁纸我使用了 swaybg,因为它支持不同显示器使用不同的壁纸,这样我的 e-ink 墨水屏就可以独享纯白壁纸了~或者什么时候我专门找张合适的黑白壁纸也挺好的。

fcitx5 输入条的文字 padding 太大。鉴于我现在日常使用 Wayland 了,我改了我这个主题,适配 Wayland。X 那边也没有太难看。

fcitx5-paste-primary 已经添加了 Wayland 支持,虽然实现很不优雅……

看图软件,使用 imv(同时支持 Wayland 和 X)取代了 sxiv。许多图片要预览的话,thunar 或者 geeqie 也挺好的。

以及一些已经报告的 bug:

还有一些未报告和未调查清楚的 bug,等事情明了之后我再更新啦~

by 依云 at November 20, 2021 01:45 PM

November 15, 2021

百合仙子

Wayfire 迁移进展

本文来自依云's Blog,转载请注明。

这几天又解决了一些问题,记一下。

Wayfire 部分:

  • 部分按键绑定无效的问题,Super+数字键无效是因为这个是 git 版本才有的。PrintScreen 无效是因为需要写成KEY_SYSRQ……
  • 窗口标题的问题。显示中文的 pr 已经提交。也把 scale 插件的问题一起修了。
  • 要不显示标题栏也很好办,首先 preferred_decoration_mode = server 让窗口们都用 wayfire 画的装饰,然后设置 height = 0 这样就看不到标题栏啦(窗口边框还留着;连边框都不想要的话可以不加载这个插件就好了)。
  • lightdm 启动不了 wayfire 的问题,在 ~/.xprofilesleep 1 就好了。相关 issue:Missing some input devices in wayland session · Issue #63 · canonical/lightdm
  • 嗯,lightdm 是会给 Wayland 会话 source ~/.xprofile 的。所以在里边判断 XDG_SESSION_TYPE 环境变量然后做相应的处理就好了。另外 sddm 是会 source ~/.profile 的。
  • invert 和 zoom 插件只支持一个显示器的问题,没有再次复现。可能是 git 版修了吧。
  • 挂起之后恢复,没键盘鼠标的问题大概也好了?今天我只遇到过一次没键盘,重新插了一下……
  • resize 调整窗口时保持比例(比如用于 scrcpy)。我已经在自己的 fork 中加入这个功能。
  • 锁屏使用 swaylock。不过它只锁屏并不会关显示器,所以我又写了个 xset dpms force off 的等价程序
  • HiDPI 下 Xwayland 窗口是糊的。通过改变插值算法(默认的 GL_LINEAR -> GL_NEAREST)来缓解。sway 默认就支持这个。这个 nearest 算法在整数倍放大时,会不那么糊,不过颗粒感会很明显(就是把显示器分辨率给降回去啦)。
  • 哦,还有个 git 版本的新问题:slurp 或 swaylock 在运行时,会消耗不少 CPU。我发现是由于 wayfire 一直在发送 configure 事件造成不断地重绘,已经给补上并提交 pr 了。

我的 Wayfire fork 位于 https://github.com/lilydjwg/wayfire/tree/lilydjwg,里边有什么请自行看提交历史。不过要注意的是,这个分支我可能会 push -f 以清理历史。

应用程序部分:

  • flameshot 需要设置 XDG_CURRENT_DESKTOP=sway 才能工作,然而在多显示器的时候只会给用户编辑左上角的部分,还是没法用。于是我用回传统的「选择+截图」组合了,只不过在 Wayland 下是 slurp + grim 这个组合。
  • wl-paste 和 xsel 不同步的问题,是由于 wlroots Xwayland 在窗口没有焦点时,被禁止与 Wayland 部分同步剪贴板。
  • 我装好 xdg-desktop-portal{,-wlr},设置好 XDG_CURRENT_DESKTOP 环境变量,然后重置了一下 obs-studio 的配置文件之后,它能工作了。不过只能录整个屏幕,不能按窗口录啦(由于更容易意外录到别的内容,反而不那么安全了)。

然后是剩下的问题:

  • spicy 无法捕获键盘是因为 wayfire 没有实现那个协议。有空我去 patch 一下好了。
  • 火狐还是不能录屏。听说是协议有更新火狐还没跟上?
  • wireshark 的菜单会显示在屏幕最右边。
  • mako 的通知文字经常是糊的,reload 一下什么的可能会好。也遇到过它不显示,reload 一下又好了的情况。
  • GTK 3 程序的右键菜单,上下会多出一部分内边距并被加上的圆角。圆角我还能忍,但多这么一部分不能选中就很难看了,然后火狐的多级菜单还没把这个考虑进去,没对齐各级菜单……

解决这各种问题挺累的,我就不仔细核查和整理了。本文只是个记录,把已经完成的事项从我的 TODO 列表转存到博客而已啦=w=

by 依云 at November 15, 2021 11:36 AM

November 12, 2021

百合仙子

不同情况下的图形效果

本文来自依云's Blog,转载请注明。

我在上一篇说到,Wayland 下再也不会遇到撕裂了。后来群友说,这是 Intel 的 modesetting 驱动特有的问题。所以我又重新比较了一下。为了让事实说话,而不是靠很容易有偏见的主观感受,我使用 Sony Xperia XZ2 Compact 手机的「慢动作」功能,以 960fps 录制了不同情况下,火狐滚动同一页面的效果。不过视频我就不放了,上传费时又占地方。

首先是我长期使用的组合:X11 + Intel 集成显卡 + modesetting DDX,窗口管理器是 Awesome 3.5.9,混成器是 picom。这种情况下有非常严重的斜线撕裂,在画面内容变化大的时候(如无过渡切桌面、视频镜头转换、大幅度滚动页面)必现。撕裂的样子如下(「慢动作」的分辨率有限,亮度低是预期现象):

X11 Intel 上的画面撕裂

可以很清楚地看到,截图中的页面是由两帧的内容拼接而成的。而且左下的是后一帧,右上的是前一帧,我不知道为什么会是这样子。我记得以前它不会撕这么大条斜线的啊,是斜-水平-斜的样子。反正都很难受就是了。我以前以为这种撕裂是时不时出现的,但多次「慢动作」慢放表明,它发生的频繁度大大超出了我之前的感受。

这个组合还有个问题是:外接显示器时,鼠标会跟着画面的更新而闪烁。画面更新越多越频繁,它闪烁得越快,而且和更新的区域也有关系。因为眼睛总跟着鼠标跑,所以即使它大部分时候不影响定位,但对主观感受的伤害应该也挺大的。

然后 Wayland + Intel 集成显卡,混成器是 Wayfire。完全观察不到撕裂。鼠标光标也很稳定不闪。

X11 + Nvidia 独立显卡 + 官方闭源驱动。还是 Awesome + picom 的组合,输出还是经过 Intel 显卡。也完全观察不到撕裂。不过有个更严重的问题——我的外接显示器每十来秒会黑屏几秒。听说把显示器关掉重开就可以解决,不过我无意使用这个方案,就不理它了。除了我之前提到的容易崩溃之外(其实我也不知道现在它还崩不崩),这显卡不支持视频硬件加速。这意味着我根本没办法看 4k 视频。PS: 火狐的 WebRender 能正常自行启用。

X11 + Intel 集成显卡 + Nvidia PRIME Offloading。我只是好奇地试一试这个方案啦。以前这个方案里火狐没办法启用图形加速,只能用「basic」渲染器来着。现在火狐有了 Software WebRender,倒是也能比较好地跑起来了。依旧会撕裂,好像比用 Intel 显卡要好那么一点点?

然后对比一下水族馆的图形性能数据:

  • X11 + Intel 显卡,30fps 左右。
  • Wayland + Intel 显卡:接近 60fps。
  • X11 + Nvidia 显卡:30-40ftps。
  • X11 + PRIME,这个比较令我意外,竟然也在 30fps 上下。要知道这个是纯 CPU 实现的,没有 GPU 加速的呀。

其实 WebGL 我用得不多啦(Google 地图更常卡在网络 I/O 上而不是渲染上)。更多的是播放在线(YouTube)视频啦。

  • X11 + Intel 显卡,1080p 60fps,GPU 用满,丢帧三分之一!4k 60fps 也差不多。原来重点不是分辨率(反正 GPU 的解码能力还没用满),重点是视频的帧率啊。
  • Wayland + Intel 显卡,4k 60fps 都不怎么丢帧,更不说其它了。GPU 图形计算用到一半左右。
  • 没有更多方案了。我这 CPU,软件解 4k 会卡成 PPT 的。

综上,Wayland 的表现是最好的!好吧虽然遇到了挺多问题的,不过我大概都能修或者绕过。虽然撕裂不全是 X11 的错,但是确实有客观证据证明它体验不好,不是 Wayfire 的特效太绚烂太怀旧让我偏心了~

by 依云 at November 12, 2021 01:27 PM

November 09, 2021

Alynx Zhou

把我的时间戳还给我!

当然,我在标题里说的时间戳并不是狭义的 UNIX 时间戳,我只是想表达一下精确的时间记录而已。

不知道从什么时候开始,许多网站都不再显示 2021-11-09 09:00:00 这样精确的时间,而是开始显示“刚刚”、“5 分钟前”、“3 个月前”、“1 年前”,我能猜出来这又是哪些自以为聪明的产品经理以“对用户友好”的理由想出来的或者抄来的,但不幸的是大部分这种行为都很愚蠢。据我所知 Twitter 是这样,Twitter 做什么就抄什么的微博也是这样,令人难以忍受的是 GitHub 也是这样显示时间,以及今天彻底惹恼了我的 YouTube 在视频页面也是这样显示。

如果你还没搞清楚我的愤怒来源,我实际上是想做这样的事情:我知道索尼 A7S3 是在 2020 年 7 月发布,我想快速找到相关的视频,请问我怎么在这一大片的“1 年前”里猜到去年 7 月的大致位置?

全都是 1 年前

“1 年前”这个描述可以包含从“1 年零 1 天”到“1 年零 364 天”这样不精确的时间范围,我觉得和直接不显示时间范围也没什么区别了。或者像 GitHub 那样鼠标悬浮在上方显示时间戳的方案也不好,比如这个页面这么多视频,我的目的是一眼快速找到我需要的时间段,一个一个悬浮多慢?所谓的用户友好?不见得,用户要被气死了。人类的脑子还没弱到连算个大致的时间差都算不出来。从工程上来说,这还引入了额外的复杂度,比如有一个 JS 脚本是专门用来更新这些时间的,你需要引入额外的代码,才能保证从“刚刚”变成“7 秒前”、“10 秒前”、“不到一分钟”、“5 分钟前”(说的就是你 GitHub),哈哈,这简直太好笑了。更别提弱智的微博 API 竟然直接在 JSON 里面返回“X 分钟前”,不过反正这种互不联网说不定什么时候就把 API 给砍了。

我觉得 Twitter 和微博这种本身就是短平快讲究时效性的网站使用大致的时间差而不是时间戳还算可以理解,至于 YouTube、 GitHub 这种经常需要查看精确时间的显示大致的时间差纯粹是给用户填堵,这一点 B 站经常脑残的前端反而做得不错,在视频页面显示的是精确的日期。

by Alynx Zhou (alynx.zhou@gmail.com) at November 09, 2021 01:19 AM

NVIDIA 驱动和 GNOME 和 Wayland

更新(2021-11-09):最新的 NVIDIA 495 驱动终于支持了大家都在用的 GBM,同时最新的 XWayland 21.1.3 也添加了这方面的支持,也就意味着 NVIDIA 用户不再需要单独的轮子而是使用现有的稳定的代码。我已经切换到 GNOME Wayland 不止一周,目前各种功能都很正常。


由于各种各样的历史原因和近期的变化,我在最近的聊天里发现很多朋友对 NVIDIA 驱动对 Wayland 的支持情况不甚了解,正好我最近在折腾相关的东西,所以打算简单介绍一下我了解的。

常见问题

为什么 NVIDIA 的 Wayland 支持这么差?

长话短说,大部分驱动(AMD 的开源和闭源驱动,NVIDIA 的开源驱动 nouveau,Intel 的开源驱动)都同意采用 GBM 作为缓存 API,但是 NVIDIA 像个赌气的小孩一样表示“你们这个不好,我要做个更好的 EGLStreams”所以不支持 GBM,于是所有要兼容 NVIDIA 闭源驱动的混成器都要为它做单独的实现代码。诸如 GNOME 或者 KDE 这种大型项目有足够的人手,同时 NVIDIA 似乎也提供了一定的帮助所以已经有了混成器内部的 EGLStreams 实现,但也有些小型项目并不支持这个玩意。而且 NVIDIA 吹牛皮吹得很大,EGLStreams 哪里“更好”从他们的龟速进展上根本体现不出来。可能是因为挖矿赚得盆满钵满,忘记了显卡的本职工作是显示,NVIDIA 的闭源 Linux 驱动质量一直停留在能用的程度,同时又做各种各样限制导致开源的 nouveau 在较新的显卡上几乎不能用。

NVIDIA 闭源驱动之前一直是稀泥巴糊不上墙的垃圾,因为它在 Wayland 下对使用 XWayland 的程序没有硬件加速支持——也就是说如果你使用 Xorg 会话,程序是有硬件加速的,你使用 Wayland 会话,Wayland 程序也是有硬件加速的,但是在 Wayland 会话下面那些旧的 Xorg 程序就没有硬件加速(听起来就像捡了芝麻丢了西瓜一样)——而仍然有相当一部分旧程序在 Wayland 下依赖 XWayland 运行,所以 NVIDIA 闭源驱动的 Wayland 会话基本可以认为是不能日常使用的。

从版本 470 之后 NVIDIA 闭源驱动有所改善,首先是他们的员工提交的补丁在 XWayland 21.1.1 版本释出了,这个补丁添加了对 XWayland 硬件加速的支持(需要新版本驱动),然后新版本驱动在上周变为稳定版,用户可以安装,所以在拖了至少三年之后最后一块空缺终于被补上了。使用 KDE 和 GNOME 的 NVIDIA 显卡用户理论上就可以获得完整的 Wayland 体验了。

现在 NVIDIA 用户的 Wayland 会话可以日常使用了吗?

更新到 470 驱动之后我第一时间体验了一下,结果就是很抱歉,还是不行,不过 我遇到的都是一些小问题:

  • Firefox 稳定版没法正常工作,但是 Nightly 版本是可以的,原因是 NVIDIA 和 Mesa 的 EGL 实现存在一点小区别,修复补丁已经在 Nightly 里面了,我们只要等稳定版版本升上去就可以了。
  • GTK3 程序都能工作,但是 GTK4 的程序(比如 GNOME Extensions)却全都挂了,原因应该和 Firefox 一致,修复补丁也已经合并了,只是还没有释出稳定版。
  • Google Chrome 不能工作,但是比较新的 Chromium 似乎可以,原因推测也是一样的,我懒得查了,同时 所有一直抱着旧版 Electron 不更新的程序应该都有同样的问题。
  • GNOME Shell 的缩略图会显示错误,看起来是个两年的老 bug,最近开发者似乎找到了原因,还没修复。

不过总而言之这些都是能很快修复(废话,大家动作都比 NVIDIA 快,建议老黄把自己的皮衣换成乌龟壳)的问题,不至于被卡很久,至于你的发行版是那种追求“稳定”选择了一个不支持的大版本然后不更新的?哈哈,关我 X 事。

更新(2021-09-17):Arch 这边 Firefox 更新到了 92.0,所以已经修好了。Google Chrome 现在是 93.0.4577.63,我也能正常打开。GTK4 的 MR 已经释出了稳定版,所以没问题。Atom 在我这用的是 electron9,能用,反而是用 electron12 的 Code - OSS 不能用了……最后那些不知道打包了几百年前的闭源拖拉机(要不把拖去掉变成垃圾?)也就是我指的很明确的 Slack 不能用,加上 --disable-gpu 是能用的,但是是他的问题,为什么要我关掉硬件加速消耗 CPU 资源?最后 GNOME Shell 自己的那个 bug……目前没有开发者搞这个,我简单看了一下发现是个我没涉足过的领域,不过总之我觉得是个看起来不爽但不影响你实际使用的问题,所以现在的状态就是 “又不是不能用” !(我可没说后半句啊!上一个说了后半句的珠海小厂现在还活着吗?)

另外我在 openSUSE Tumbleweed 上测试的时候还遇到一个问题啊,它默认安装的是 nouveau,需要手动在 YaST 里面安装 NVIDIA,装完之后似乎两个驱动冲突把我显示器搞黑屏了,于是我就没法直接重启。而且在那之后我显示器神奇的再也不亮了(PS4、Switch 全都点不亮它),拔掉电源放半分钟再插上才好,很头疼啊。

我是 GNOME 用户,为什么装了 470 版本 GDM 选单里的 GNOME 还是 Xorg 会话?

这个有几个原因,首先就是 Wayland 需要 Kernel Mode Setting 的支持(这也是个 NVIDIA 拖了好久才补上的特性),所以你的内核参数要有 nvidia-drm.modeset=1。其次要看 /etc/gdm/custom.conf 里面 WaylandEnable=false 是不是被注释掉的(默认应该是)。

最可能的是 udev 规则,GDM 带了一个 /usr/lib/udev/rules.d/61-gdm.rules 文件,里面有这么几行:

# disable Wayland on Hi1710 chipsets
ATTR{vendor}=="0x19e5", ATTR{device}=="0x1711", RUN+="/usr/lib/gdm-runtime-config set daemon WaylandEnable false"
# disable Wayland when using the proprietary nvidia driver
DRIVER=="nvidia", RUN+="/usr/lib/gdm-runtime-config set daemon WaylandEnable false"
# disable Wayland if modesetting is disabled
IMPORT{cmdline}="nomodeset", RUN+="/usr/lib/gdm-runtime-config set daemon WaylandEnable false"

第二行表示如果检测到 NVIDIA 闭源驱动就关闭 Wayland,第三行则是如果没开启 Kernel Mode Setting 也关掉 Wayland。

我猜大部分用户都没有第一行的设备,所以我建议直接 ln -s /dev/null /etc/udev/rules.d/61-gdm.rules 屏蔽掉这个文件。

别问我既然 470 驱动都出来了 GNOME 开发者为什么不删掉这个文件,NVIDIA 憋出来 470 驱动总共也就一周多点,还有一堆小问题呢,删了怕不是又有一堆人来骂“怎么又把不稳定的东西搞给我了”。

我是笔记本双显卡用户,主用的是核芯显卡,为什么我 GDM 选单里的 GNOME 也是 Xorg 会话?

检查一下你是不是用了 NVIDIA 之前提供的 Xorg 下面的双显卡管理功能(有群友说叫 prime,我不知道是不是叫 prime)(顺便一提这也是 NVIDIA 憋了好几年才憋出来的本来就该有的东西)?如果你使用那个,首先它是 Xorg 下面的支持,其次它会加载 nvidia 模块,自然就会落到上一个问题里面的 udev 规则上。

有群友在群里表示他删了那个规则然后进 Wayland,可以是可以,但我感到很迷惑。这个 prime 功能 NVIDIA 之前只是说了在 Xorg 下面可用,所以我不是很理解又要 prime 又要 Wayland 的可行性,我也不知道 NVIDIA 的按需渲染现在到 Wayland 里面还能不能像之前 Xorg 里面提供的功能一样正常工作。就理论上我给 NVIDIA 掏了钱,这是他的义务给我支持这个功能,但这可是垃圾 NVIDIA 啊!

你问我有没有什么建议?如果你需要这玩意,就按照 GNOME 的规则用 Xorg 就好了,没错虽然我是 Wayland 支持者,但我在这里建议你还是先用着 Xorg。如果你只是想用 Wayland 然后并不是很需要用 NVIDIA 显卡,那就关掉它不让它加载驱动,没错,我在自己的笔记本上用的是 Bumblebee。

我是 KDE 用户,你扯这么多 GNOME,跟我说说 KDE 现在有什么问题呗?

大哥,我是 GNOME 用户啊,既然你是 KDE 用户,建议你自己试试然后给我讲讲……

没用的观点

你看这个 GDM 就是逊啦,我这里卡死根本不能用,大家都推荐换 SDDM 巴拉巴拉……

强调一下我的目的是解决问题,比如群里有人问为什么我双显卡 GDM 里没 Wayland 可选你来这么一句我觉得很不礼貌,当别人就是需要用 GDM 的时候说“大家都推荐换 SDDM”我觉得不管是 GNOME 用户还是 KDE 用户都会觉得不是个好回答。其次只说自己 GDM 卡死完全没法判断问题所在,别人都没这个问题,或许问题就不在 GDM 而在你的配置呢?同理,这个“大家都推荐换 SDDM”里的“大家”是哪里来的呢?我觉得这样的回复对解决问题没什么帮助。

Xorg 好!Wayland 坏!

请列举出充分而恰当的理由支持你的观点,而不是人云亦云。比方说你可能觉得 Wayland 缺乏你需要的某些功能,这可能是设计理念上的不同,或者发展时间不够所导致的,毕竟 X 协议的历史比 Wayland 长得多,所以请多一点耐心。而至于不讲道理或者二极管或者言论不友善的行为在哪里都是不受欢迎的。

我也是 NVIDIA 用户,我玩 XX 游戏卡成幻灯片了!

……信息太少,无法给出合理推断,只能告诉你我游戏玩的可欢乐了!

你们 NVIDIA 用户真麻烦,谁叫你们自己掏钱买 N 卡受罪!

你又不知道老子都有什么需求,买 N 卡我是暂时没法用 Wayland,但是不买 N 卡我就要达芬奇受罪 Windows 下面打游戏受罪直播编码受罪跑 hashcat 受罪(存疑)。我做的选择是我权衡之后的结果。再说一遍,解决问题和“换 XX”是完全不一样的态度。

所以就是钱是掏了,骂还是照样骂,而且我都掏钱了,骂得然更有底气了!

by Alynx Zhou (alynx.zhou@gmail.com) at November 09, 2021 01:10 AM

November 08, 2021

Phoenix Nemo

笔记:Arch Linux iPXE 基本启动脚本

似乎一直没有(公开且版本够新)的 Arch Linux 无人值守安装配置,所以想做一个。

参考 netboot.xyz 的 iPXE 配置,简单记录一下:

1
2
3
4
5
6
7
#!ipxe

set mirror http://archlinux.mirror/archlinux/iso/latest
set script http://unattended.install.script/script.sh
kernel ${mirror}/arch/boot/x86_64/vmlinuz-linux archisobasedir=arch archiso_http_srv=${mirror}/ ip=::: BOOTIF= net.ifnames=0 script=${script} mirror=auto
initrd ${base-url}/arch/boot/x86_64/initramfs-linux.img
boot

参考 SYSLINUX 的 PXELINUX 部分和内核 ipconfig 部分的文档写 ip= 参数的时候一边拿不到 DNS 一边疯狂报过多参数,最后发现是这个 bug 的锅。而且它还被 netboot.xyz 在脚本里注释出来了我可能需要去检查一下视力

总之目前暂且只能用 DHCP 来在启动过程正确配置网络,否则无法下载系统镜像。

截至写这篇的 Arch Linux 的官方说明在这里

最后就是自动安装脚本了,这个坑暂且撂在这里,以后有时间了再慢慢填…

咕咕咕

似乎一直没有(公开且版本够新)的 Arch Linux 无人值守安装配置,所以想做一个。

参考 netboot.xyz 的 iPXE 配置,简单记录一下:

November 08, 2021 07:16 PM

百合仙子

Wayland 初体验

本文来自依云's Blog,转载请注明。

前天得知 wayfire 能够直接在 X Window 下运行之后,我就尝试了一下。很好,能跑。虽然不能捕获键盘,从而与外边窗口管理器重复的快捷键无法使用,但至少它的鼠标光标位置是对的,能够用来测试。我在 QEMU 里跑过 KDE Wayland、sway 和 wayfire,无一例外鼠标位置不对。KDE 里是缩放倍数设为 2 之后,鼠标的实际坐标会翻倍。后两者基于 wlroots 的,鼠标的实际坐标位于显示的光标上方几十像素处。总之没法用。

在 X Window 里粗略地配置了一下 wayfire 之后,我就想实际在外边跑跑看。结果被惊艳到了,感觉就像年轻了24岁一样!

优势

尝试几次之后,我终于把 wayfire 跑起来了。

首先是火狐图形性能翻倍!

我打开火狐,跑了一下 WebGL 水族馆(微软的 FishGL 没啦)。接近 60fps,是我在 X Window 下跑的两倍有余!再试试 YouTube 的 4K 视频,竟然几乎不掉帧!我的电脑的性能原来这么好的吗……在 X Window 下,火狐播放这 4K 视频,会占满 GPU,并且 CPU 也几乎用满,丢掉约十分之一的帧。而在 Wayland 下竟然只用了一半的 GPU,CPU 用量也不怎么高。而且操作起来很流畅,右键菜单一点就出来,鼠标划过菜单项,高亮也跟得很紧。

画面再也不撕裂啦!

一直以来,我切换窗口、播放视频、快速滚动窗口内容的时候,时不时能见到画面撕裂。一般是在中间附近,一道水平接着倾斜接着再水平的裂痕会一闪而过,其上和其下是两帧不同的内容。读过 xplain 以及 Wayland Book,我知道这是由于 X Window 服务端与客户端之间并不同步,显示画面时,缓冲区里有啥画啥,并不在乎客户端有没有画完,导致相邻的两帧拼接在了一起。而 Wayland 会使用双重缓冲,客户端在缓冲区里画好了,才告诉混成器这一帧画好了、可以显示出去了。而在画好之前就显示上一次提交的缓冲区内容,所以永远不会出现画一半的情况。

我的光标也不再闪烁啦!

我也不知道为啥,我在 X Window 下,特别是接外接显示器的屋里,鼠标光标会像遥远的星星一样闪啊闪的,有时候闪得甚至影响我操作。我用的 picom 混成器的选项我改来改去,不光没能解决这个问题,反而是搞得它时不时崩溃一下了。我也不知道这是怎么回事,按理说鼠标光标由硬件绘制的,不应该时有时无啊。而在 wayfire 里,光标一直很稳定,从来不闪烁。我是返回到 X Window 下才注意到这一点的。

特效!

尝试 wayfire 的一大原因是我看中了它的特效。当年我初入 Linux 的时候,就挺喜欢 compiz 的特效的,只是后来因为性能问题才转到 Awesome 的。wayfire 有好多我当年常用的特效,果冻、火焰、立方体、展览,还有那个翻页一样的切换效果叫啥来着。没有纸飞机倒是有点可惜。

其它的 Wayland 混成器,KDE 我已经尝试过了,虽然是 X Window 版本,但功能应该是一致的。同样 sway 的对应 X Window 版本 i3 我也尝试过了。结论是,我并不是很在意平铺,但是我很在意键盘操控性。wayfire 也有较为丰富的键盘操作设置,应该也可以通过插件来扩展。

已解决的问题

虽然有上边这些优势,我还是回到 Awesome 来了,因为还有不少问题和需要配置的地方呢。此节记录一下我已经解决的问题。

  • GVim。因为 GVim 使用的是 GTK,所以改一改让它支持 Wayland 并没有很难。我改好的版本在我的 vim fork 的 wayland 分支上。目前还缺少两个重要功能:终端里的剪贴板,以及 +clientserver。
  • mpv 是糊的。研究发现这是因为我加了no-hidpi-window-scale参数。它是为了解决 X Window HiDPI 下窗口过大的问题,但到了 Wayland 下使用它就会导致并不进行 HiDPI 缩放了。
  • 窗口焦点不会跟随鼠标。这是在平铺式窗口管理器里使用很广泛的特性。wayfire-plugins-extra 里有个 follow-mouse 插件可以实现这个。
  • GTK 的鼠标主题、禁用动画设置等不生效。原来在 Wayland 下 GTK 不读取~/.config/gtk-3.0/settings.ini,可以使用 dconf-editor 去编辑 org.gnome.desktop.interface 下的选项
  • xfce4-notifyd 的通知会被当成普通窗口,置于屏幕中间并且有标题栏。有一个 fork 修复了此问题,不过我还是决定使用更轻量的 mako(不是 Python 的那个模板引擎哦)。
  • Xwayland 跑 PRIME offloading 时,Minecraft 的画面会来回抖动。于是只好放弃 NVidia 显卡改成 Intel 显卡了(反正自从我买了 4K 显示器之后就都跑不动了 QAQ)。
  • fcitx5
    • GTK 中,在输入编码的时候,按一次键闪烁一下。csslayer 说是不这样做就无法移动窗口。于是我改了一下代码,选择移不动。反正也只有在屏幕边缘的时候才需要移动。Qt 那边这个问题倒是不大,因为 fcitx5-qt 总是在应用程序的窗口范围内绘制候选词窗口,能够判断什么时候需要移动,所以只是在屏幕边缘的时候闪一闪。
    • Qt 中(就是 Telegram 啦),在窗口下方输入的时候候选词窗口会悬在文本上空。我改了一下代码,现在能够准确定位到文本的上方紧贴着了。代码已经 pr。
    • fcitx5-configtool 不显示部分图标。把GNOME_DESKTOP_SESSION_ID=0环境变量加上就好了。这个环境变量莫名其妙地解决了不少问题呢。

剩下的问题

我暂时还是以 Awesome 为主,因为还有很多问题有待解决:

  • Vim 不支持剪贴板、clientserver 特性。我打算通过另外的方式实现。工作量有些大。
  • SPICE 客户端 spicy 无法捕获键盘,造成虚拟机里无法使用与外边相同的快捷键。
  • LightDM 启动 wayfire 会话时会立即退出,而实际上 wayfire 是已经启动了的。各种日志里均没有找到任何有用的信息。
  • wl-paste 获取的剪贴板内容与 Xwayland 的并不同步。似乎大部分程序都支持两种剪贴板协议,但是 wl-paste 和 Xwayland 各支持其中之一?
  • Xwayland 不支持 HiDPI。有个补丁,但是会造成窗口一直缩小再缩小。
  • fcitx5
    • 候选词的内边距有些大。有空再调了。
    • fcitx5-paste-primary 不支持 Wayland。有空再加了。
  • wayfire
    • 窗口标题不支持中文。我倒是打算给 wayfire 加个不显示标题栏的选项。
    • 的 fast switch(真的很快)会导致全屏窗口取消全屏。
    • 似乎不支持触摸板拖拽的时候中断一下。应该很好修补吧?
    • 只能切换到本工作区的上一个窗口,不能跨工作区
    • invert 和 zoom 插件只支持某一个显示器。前者我用不到,但后者应该还有些用处的。
    • 设置的 Super+数字键 切换工作区无效。
    • 我还没想好我那些环境变量去哪里设置比较好。~/.xprofile没啦,~/.pam_enviroment我也不想用。在考虑使用 systemd 那个功能,或者我自己 wrapper 一下 wayfire。

缺憾

  • 使用 libinput 取代了 synpatics,非常好用的画圈滚动(一直画、一直滚)没了。

结语

还有挺多东西要配置的。锁屏啦,壁纸啦,还有未发现的问题啦,等等。慢慢来吧。Wayland 真的挺顺滑的。

对了,有个有意思视频给大家看一下。

火狐着火啦~

这是我还没运行通知守护进程时,火狐发出的通知窗口,不知道为什么被反复创建和销毁。

by 依云 at November 08, 2021 12:55 PM