Recent Posts

's avatar

Reappreciating Monika's Wisdom

Four years ago, when I was an undergrad and played Doki Doki Literature Club. Deeply moved by her words, I went out to make a Pacman packaging script for a Monika fortune mod. So that any time I type fortune monika, a paragraph of Monika’s talk appears.

The packaging script grabbed from the repository of a fan-made mod Monika After Story – they kept a copy of the original game’s dialog in that chapter. During the years they have changed the file structure, and is no longer including the code verbatim in the master branch, but the file is still accessible.

According to Team Salvato, DDLC’s developer IP guideline, MAS can host the code because it is used for a mod of the game. Also, the packaging script is neither written by them nor for sale. Therefore, it seemed to be compliant so far.

Anyways, allow me to randomly choose some of them and share my thoughts now, four years later. And there are a whole bunch of sayings out there. And Monika After Story has been expanding it – how exciting it is! I imagine one day the fortune mod will include those new words of wisdom as well.

's avatar

4-9-2023 Diary


Date: 04/09/2023 Last edited time: April 9, 2023 7:50 PM Weather: 🌞Sunny

I had a longed-for eight-hour sleep yesterd

's avatar

2023-04-18 Diary


Date: 04/08/2023Last edited time: April 9, 2023 9:58 PMWeather: ☁️Cloudy

The Internet is becoming our echo chamber. That is true. But anyone who is not satisfied with it, who goes out of their comfort zone to broaden their views, deserves respect, not hindrance.

Gris GifTwitter

Twitter does not allow anyone like or retweet Substack’s this tweet, nor my quoted retweet of it.

I felt sad for Twitter. It had to go out of its way to stop people from turning away.

Elon Musk promised it to be ‘open’, but social media can never be too closed — they want to close people inside them. It is a place where people only see what they agree with. If you already agree with it, why would you try to think otherwise? It is such a comfortable cage that you want to stay there forever.

I recall the Eagles’ Hotel California:

You can check out any time you like, but you can never leave

And all social media want to be our Hotel California.

While I use Twitter from time to time (for cute and sexy illustrations), I do not think it is good to be caged by it.

Steam banned my review of Glare1more. When I read it again tonight, I thought it might be because I put the link to the developer’s home page.

I happened to use Substack for a minute or two. I wanted to find a new place to put my writings in, and I searched everywhere.

Hatena, Medium, Note, Substack, Blogger, Ghost, and the list goes on.

Depending on my mood, sometimes I write in Chinese. And when I do, I want it to look good. These blogs share a feature, “What you see is what you get”. So, when I saw Chinese characters stand out like Yao Ming among some kindergarten kids, I knew everyone would see the same. It hurts my eyes so much that I want to cry.

It takes no effort to give Chinese characters a closer look to Japanese ones, so they look good side by side.

But none of them cares.

Or it is just me — my computers and my phone use English because I prefer everything to be in the same language. And being English, they do not know about Chinese characters, so they do their best — which is another way of saying do their worst.

Using English on my phone also hurts me in a way I haven’t thought of:

I bought most of my Japanese books through Bookwalker. But Bookwalker must be unhappy with me using English — it does not want to show the Japanese books but its translated books in stead. I like reading books or playing games in their original languages.And it’s 12pm now. I missed Duolingo’s booster. I will get it tomorrow.

The restaurant that I wanted to visit closes on Saturdays — I will be back…

I had hot pot with my roommate for dinner. Recently we ate together twice, but not in the last two years. That is something notable.

After dinner I went to school — I feel it better than spending time at home, looking at social media. If only I could read books at home.

's avatar


I bought Fedora Remix for WSL. I feel the urge to support. Dev tool companies need money to run, but people are much less fancied by the thought of paying them even if the asking price is as few as ten dollars.

For the first time this week — ok not the first time I’m sorry — I did Duolingo in the morning. Recently it has introduced a new mechanism: if you use it before 12pm, you can get an experience booster after 6pm; and if you use the app after 9pm you will be awarded another one the following morning. It is super powerful as it doubles the experience for 15 minutes, so people are encouraged to do it once in the morning and once at night. It is such a neat way to motivate people to form good habits.

I asked ChatGPT for some wearing advice, and I found out my need for leggings or tights — they seem so helpful for a ten degrees Celsius day like today, but only women are known to wear them. This fact bothers me so much.

I listened to the Framework staff’s advice and took away a memory stick from the laptop. The result: it just worked. No Bluetooth lag or disconnection, no forceful shutdown — I am just over the moon.

On my way to the school, I checked passersby’s bottoms for reference. Most women wear leggings or jeans. Men wear jeans and trousers — I can’t see through them, but they should be thermal underwear. And there are people wearing shorts! How brave! If they are brave enough to wear shorts on a cool day like today, wearing leggings should be a piece of cake.When I charged the laptop, the Bluetooth mouse was unwilling to talk to it again. It is too early for joy. Speaking of joy, I have a gathering to attend tonight. Have some drinks and play some Mahjong. I’m really looking forward to it.

Who would have thought the problem a student ran into took an extra hour?

Anyways, I arrived at my friend’s apartment at 7pm. We played a lot of pizza and ate a lot of Mahjong. The pizzas are two macaroni — I mean pepperoni — and one vegetable. They were tasty. And I drank a can of Coors Light. I played until 12:48am, biked my way home, and am deadly sleepy now. If I wrote something wrong, please forgive me.

We played Yonma and in the last, Sanma. In the final game I played, I won a hand of toitoi hatsu dora 5 which was worth 24000 points. But the player dealt in 24000 managed to pull a comeback to second place by winning a 12000 hand from the other player. “Rrrrrrrigged!” He happily said it when opening his hand. A large part of Mahjong is based on luck, so they often say it is “rigged”, parodying Trump’s claim of a rigged election.

Speaking of Trump, now he turned himself to the court. Although I do not hold a good impression of him, I do hope that he gets what he legally deserves. “We are embarrassing ourselves,” Trump said in a speech after the hearing. But he himself is the man who made the spectacles first.

's avatar

博客Plan B


Plan,综上 B

's avatar

博客Plan B (复原)

这篇博客是在Decap CMS上写的。



综上,Plan B大告失败!

图里从里到外是:note(没截全),substack, hatenablog,medium。



's avatar


在写这篇文章之前,我折腾了一下午的npm。改了依赖,扔了失修已久的multilingual feed,为了解决一个错误听ChatGPT的话改了tranquilpeak主题一通,最后告诉我只要把strip-indent的版本改回3.0.0就行了。现在这个时间点再运行npm audit,应该是0漏洞了。








一开始是在12美元一年的Ethernet Servers,搞个Nginx挂着(首要用途还是开VPN),然后是HostMyBytes。


用VPS的时候还想着用Cloudflare怎么加速——结果发现一用VPN就不好使了。但是现在就没有必要了,name server还是用的Cloudflare,也是当时留下来的。

在2018年底的时候,我同时也部署到VPS和Github Pages上了(见如何被 GitHub Pages 蹬鼻子上脸。其实单用Github Pages也可以——访问noirgif.github.io就是走的pages,但是我闲得慌,为了一点现在已经没用的功能,而搞了Netlify。

HostMyBytes在19年4月被Alpharacks收购,我在那个时候改用CircleCI跑Hexo,然后CircleCI部署到专用的分支。没用Travis CI的原因,印象里是因为它的镜像太旧了还是机子太破了,还是两者皆有?当时也没人教我,只好自己试着搞来搞去,搞成现在这个模样。


半年后有了Github Actions。求你下次早点来。



  1. 老涡的博客:后来改用了Hugo。
  2. 苏卡卡的博客:不仅还在用,还自己折腾了很多,学不来。
  3. 没有第三个了。老肯老fc用的都是Pelican。



头疼的地方:NodeJS加持的Hexo有很多插件,容易发生有的包没人维护了,这个没人维护的包的依赖有漏洞这种深奥的事情。按照这个部署的流程来说我是不需要管他们的?不是很清楚。但是想本地看一看效果的时候就很麻烦。今天我为此把multilingual feed删掉了。从此这个博客更新不分语言了:明天是英语,后天是日语。本博客虽然用各种语言写的,但是对多语言的支持闻者落泪。

我也不会折腾主题。把Hymmnos字体加进来(记得提醒我玩魔塔大陆3),被Katex折腾折腾,大概就是我力所能及的范围了。曾经有的、现在已经不工作了的飘雪的代码,其实是从Winter Plus的网站那里借过来的。请不要告诉North Plus我借了代码,也不要告诉上面的人我上过Soul Plus。





Plus je écris les blogs, mieux j’aime les papier.



虽然游戏堆积成山(蔚蓝档案只是其中一个原因),但是最近有在打天使骚骚的 Demo。因为游戏堆积成山,所以还没有决定要买。



's avatar

Proposal of a New Type of Elevator that Uses Blockchain to Achieve High Availability


As you may already know, the elevator in the computer sciences department has been broken since Friday and will remain unfixed throughout the weekend. Although we have a backup elevator, it gets overloaded even on a Saturday morning. When I went to grab a cup of coffee, someone else had already ridden it, so I had to take the stairs.

We strive to create systems with high availability. A loss of three days (averaging 0.9% throughout the year) is hardly acceptable. This brings us to today’s main theme:

We present a proposal for a new type of elevator that utilizes blockchain technology to achieve high availability.

The idea behind this proposal is to create an elevator system that never goes down. By using a decentralized blockchain network, the elevator system could potentially achieve 100% uptime, with no single point of failure.


The elevator’s control system would be connected to the blockchain network. Every time a passenger boards or exits the elevator, a new block would be added to the chain. The blocks would contain information about the elevator’s current position, direction, and speed. You can think of it as a transaction: validators (a new name for blockchain miners in this context) will include it in the chain. Once six blocks are validated on top of it, we will consider it confirmed, and the elevator will move toward the destination. The confirmation of the transaction on the Ethereum Mainnet takes approximately 84 seconds. However, faster confirmations can be achieved by using private chains.

Thanks to the decentralized nature of the system, the elevator will continue to function as long as most validators are online. Even if a hacker tries to tamper with the system, the blockchain’s consensus mechanism will ensure that the elevator’s history remains accurate and unalterable. If you take a two-hour coffee break on the sixth floor, everyone in the department will know about it.

In addition, the immutability of the blockchain guarantees that there will be no disputes over the operation of elevators, which can occur with traditional elevator systems. For instance, people often disagree on how long the doors should remain closed if no button is pressed, and it is often unclear whether the “close door” button accelerates the closing. With blockchains, these rules are enforced by the system, and there is no room for interpretation.


Elevators powered by blockchain technology have a lot of potential. We highly anticipate that they will replace the current broken elevator in the CS building.

Happy April Fool’s Day!

's avatar

基于 ChatGPT 的客服系统

基于 ChatGPT 的客服系统

在 OpenAI 放出 gpt-3.5-turbo 模型之后,我们决定使用 AI 来加速目前我们重复率最高的工作:客服.

不过考虑到我们的帮助手册及FAQ总字数已经超过30万字,而 gpt-3.5-turbo 每次对话最多只能支持 4096 个 Token (经过我们的测试,这大概是三千多个简中字),还需要考虑客户的问题,回答和系统配置占用的字符.所以我们需要一个办法来让 gpt-3.5-turbo 可以在 2000 字左右时了解我们的的帮助手册.

在参考了 OpenAI Cookbook:Question_answering_using_embeddings , OpenAI Cookbook:web-crawl-q-and-a 以及 paul-graham-gpt 之后,我们决定按照以下方法来实现:

  1. 使用语雀的 API 获取我们的帮助手册及FAQ
  2. 获得到每篇文章后,按照 markdown 的标题进行分割,每个小标题对应一块语料.并在数据库中记录对应的网址和锚点,方便后续输出时同步给出参考文档.
  3. 使用 openai embeddings 获取每块语料的向量表示.
  4. 对用户的提问也使用 openai embeddings 提取向量.
  5. 使用 余弦相似度 计算用户提问和每块语料的相似度,并返回相似度最高10块的语料.
  6. 对每块语料按照相似度从高到低的顺序 对 token 进行求和,获取小于 2000 时的语料列表.
  7. 按照以下格式给与 gpt-3.5-turbo 的 system role 输入.
     你是 $$$ 系统的客服,请按照后续给予的使用手册回答用户的问题.
  8. 获取 gpt-3.5-turbo 的输出.

目前处于安全考虑, gpt-3.5-turbo 的输出仅发送到客服汇总群,并由客服人员进行回复.同时给与客服相关语料的标题及链接,方便客服人员进行回复.

依云's avatar

Linux 上的字体配置与故障排除

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


电脑系统要显示字,首先得有字体。现在 Linux 上常用的、在维护的开源中文字体就一套,同时被 Noto思源两个项目收录。Noto 系列字体是 Google 主导的,名字的含义是「没有豆腐」(no tofu),因为缺字时显示的方框或者方框被叫作「tofu」。思源系列字体是 Adobe 主导的。其中汉字部分被称为「思源黑体」和「思源宋体」,是由这两家公司共同开发的,两个字体系列的汉字部分是一样的。

Noto 字体在 Arch Linux 上位于以下软件包中:

  • noto-fonts: 大部分文字的常见样式,不包含汉字
  • noto-fonts-cjk: 汉字部分
  • noto-fonts-emoji: 彩色的表情符号字体
  • noto-fonts-extra: 提供额外的字重和宽度变种

Noto 系列字族名只支持英文,命名规则是 Noto + Sans 或 Serif + 文字名称。其中汉字部分叫 Noto Sans/Serif CJK SC/TC/HK/JP/KR,最后一个词是地区变种。


  • adobe-source-sans-fonts: 无衬线字体,不含汉字。字族名叫 Source Sans 3 和 Source Sans Pro,以及带字重的变体,加上 Source Sans 3 VF
  • adobe-source-serif-fonts: 衬线字体,不含汉字。字族名叫 Source Code Pro,以及带字重的变体
  • adobe-source-code-pro-fonts: 等宽字体,不含汉字。字族名叫 Source Code Pro,以及带字重的变体,加上 Source Code Variable。
  • adobe-source-han-{sans,serif,mono}-{cn,hk,jp,kr,tw}-fonts: 五个地区的汉字之黑体、宋体和等宽版本
  • adobe-source-han-{sans,serif,mono}-otc-fonts: 所有地区合体了的汉字之黑体、宋体和等宽版本

其中等宽版本的中文字体位于 [archlinuxcn] 仓库中。

思源汉字字体的字族名有两种,「独立包装」的版本(非 OTC 版本),是「Source Han Sans/Serif」或本地化名称、空格、地区代码(CN/HK/TW/JP/KR)。比如「思源黑体 CN」、「源ノ角ゴシック JP」等。也有带字重的别名。

而全部打包的 OTC 版本,字族名是本地化名称或者英文的「Source Han Sans/Serif」空格再加上「HC/TC/HC/K」变种代码。如果没有变种代码,则是日文变种。为了区分,香港繁体的版本附带「香港」字样,比如黑体叫「思源黑體 香港」。这些字体也有不同字重的别名。另外有个半宽的版本,是在字族名的变种代码前加「HW」字样,仅有少数几个字符是半宽的。

OTC 版本有趣的地方在于,对于大多数软件来说,不管你叫它的哪个地区的名字,它都会以设定的语种来显示。比如网页声明语种为日文(<html lang=ja>),那么不管字体指定为「源ノ角ゴシック」还是「思源黑体」或者「본고딕」,它都会「门上插刀、直字拐弯、天顶加盖、船顶漏雨」。所以用这个字体的话,不妨一律写「Source Han Sans」,然后加好语种标记。我知道的唯一例外是 mpv 的 ass 字幕文件,里边指定本地化名称的话,会使用那个语种的变体显示。

早些年还没有 Noto 和思源的时候,Linux 系统上通常使用文泉驿正黑或者文泉驿微米黑。后者是基于 Android 系统上的 Droid Sans Fallback 字体,体积较小。再之前是文鼎系列字体,也就是名字「AR PL」开头、包名叫 ttf-arphic-{uming,ukai} 的那些。



字族就是它的名字啦。常见的指代字体的方式除了字族之外还有 Postscript 名,它不含空格、使用短横线将样式附加在名称之后,比如「DejaVuSans-BoldOblique」。后者是 CSS @font-face 规则中使用 local唯一指定样式的方法(除非该字体把样式也写到了字族名里)。

倾斜就是斜不斜,英文叫「Roman」「Italic」或者「Oblique」,Italic 是专门的斜体写法(更接近手写样式), Oblique 是把常规写法倾斜一下完事。

字重就更简单了,就是笔划的粗细。常见的有 Regular、Normal、Medium、Bold、Semibold、Black、Thin、Light、Extralight 等。

详细信息可以 man 5 fonts-conf 查询。


很多时候,程序并不在乎用户具体使用的是哪款字体,像很多网站的 CSS 那样把各个平台的常见字体全部列出来太傻了,又容易出问题。所以,人们发明了「通用字族名」,也就是 sans-serif (sans)、serif 和 monospace (mono) 这些。中文分别叫无衬线字体、衬线字体和等宽字体。但是中文字体不讲衬线不衬线的,而是叫「黑体」和「宋体」(有些地区叫「明体」)。黑体常用于屏幕显示的正文,而宋体常用于印刷文本的正文。


最近有一个新加的通用字族名叫作「emoji」。Pango 渲染表情符号的文本时,会自动使用 emoji 字体。但是 Qt 尚不支持,导致有时会出问题,而将 emoji 字体排到常规字体之前的做法,又会导致数字和空格显示为全角。火狐自带了一个 SVG 格式的 emoji 字体,会自动使用。很多软件(比如 Telegram)也会使用图片来取代 emoji 字符。

CSS 4 又加了一套 ui- 开头的字族名但是除了 Safari 没浏览器支持。fontconfig 倒是可以通过配置来支持上,但是由于火狐的一个 bug 导致 ui-sans-serif 无效。

fontconfig 配置

大部分 Linux 桌面软件都或多或少地使用 fontconfig 来获取字体配置信息。其中 Pango(GTK 使用的文字渲染库)的支持是最好的。很多简陋的图形界面库则只用来读取默认字体,可能完全不支持字体回落,造成部分文字明明有字体却显示为「豆腐」。

了解了通用字族名,我们就可以为它们指定我们喜欢的字体啦。在 ~/.config/fontconfig/fonts.conf 里为每一个通用字族名像这样写即可:

  <match target="pattern">
    <test qual="any" name="family">
    <edit name="family" mode="prepend" binding="strong">
      <string>DejaVu Sans</string>
      <string>Font Awesome 6 Free</string>
      <string>Font Awesome 6 Brands</string>
      <string>Source Han Sans</string>

因为我并没有完全采用思源字体来显示汉字,所以我还是为不同语言和地区变种分别匹配了不同的字体。我完整的配置文件见:。其中,web-ui-fonts.conf 文件用于提供 CSS 4 新增的字族名,而 source-han-for-noto-cjk.conf 则使用思源系列字体来代替 Noto CJK 系列字体。



火狐浏览器,对着有疑问的字点右键,选择「检查」(也可以按 Q 键),然后看弹出的开发者工具右边的「字体」选项卡即可。鼠标悬停到下方灰色的字体名上时还能将使用该字体的字高亮显示。

Google Chrome 浏览器及其变种类似,对着有疑问的字点右键,选择「检查」(也可以按 N 键),然后看弹出的开发者工具右边的「计算样式」选项卡,拖动到最下面,可以看到使用的字体名以及有多少个字形。

至于这个字体是怎么选上的,可以切换到「规则」(火狐)或者「样式」(Google Chrome)选项卡来看 CSS 规则。搜索「font-family」看看具体被应用上的规则是哪一条。通常这里会写上一大排字体名。火狐会将正在使用的那个加上下划线,但是有时候不准确(比如该 HTML 元素使用了多种字体)。更好的除错方法是,从头到尾一个个删字体,删到哪一个时网页上的字体变动了,就说明在使用的是哪一个。我通过这种方式找出了好些我学生时代不懂事从 Windows 下复制过来的字体导致的问题。

Google Chrome 默认的字体比较奇怪,是「Times New Roman」、「Arial」和「Monospace」。见《Google Chrome 中的字体设置》一文。

Qt。其中最著名的 bug 是 QTBUG-80434 (。


使用 gucharmap 软件可以检查所有字符使用指定的字体时的渲染效果,以及它回落到什么字体上了。找到要查看的字符,然后对着它按住右键即可。

使用 fc-match -s NAME:charset=HHHH 可以查看针对指定字符的字体优先顺序,包含这个字符的字体会优先。如果不加 -s 就是看指定的模式会匹配上的字体了。其中 HHHH 是该字符的 Unicode 码点之十六进制值。如 fc-match :charset=7684 查看默认字体下「的」字会用什么字体,而 fc-match serif:charset=7684:lang=ja 查看在语种为日文的时候,使用 serif 字族名会使用哪个字体来显示「的」字。使用 fc-list :charset=HHHH 则是查看包含该字符的所有字体。


依云's avatar

新的 PaddleOCR 部署方案

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

PaddleOCR 发布 2.6 版本了,支持 Python 3.10 啦,于是可以在 Arch Linux 上跑了~所以我决定再部署一次。

我之前跑 PaddleOCR 有两个方案,使用 chroot 加一大堆 systemd 的限制选项,以及使用 bwrap 和用户命名空间。

chroot 的方案总感觉不知道限制够了没。实际上当初那篇文章写完我就意识到这服务怎么用我的 uid 在跑啊,乱发信号好像还能把我的进程都杀掉的样子。另外这个 chroot 其实是我用来学习、研究和适配 Debian 用的,并不是专门跑这个服务的,感觉有点——怎么说呢——碍事?总之不太好。

bwrap 方案更干净一些,不过创建起来挺麻烦的(所以我才只部署了一次嘛)。不使用用户命名空间可能会简单一些,但那样就是用我的用户在跑了。

所以这次我决定试试方案,使用 systemd-nspawn。另外(再次)尝试了使用 NVIDIA GPU 的版本,把我电脑上闲得发慌的 GeForce 940MX 显卡给用上了。


首先去 Arch 镜像里的 iso/latest/ 目录下载个 archlinux-bootstrap-x86_64.tar.gz 回来。在 /var/lib/machines 下创建个叫 paddleocr 的 btrfs 子卷 / zfs 文件系统 / 普通目录用来存放新的 rootfs。sudo bsdtar xf ...... -C /var/lib/machines/paddleocr 解压出来。记得一定要用 bsdtar 以避免丢失某些文件元信息(虽然我不知道那些信息有啥用但是有警告就是不爽嘛)。

然后就可以 systemd-nspawn -M paddleocr 拿到个 shell 了。这里边只安装了 base 和 arch-install-scripts。可以先修改 pacman 镜像然后 pacman -Syu python 滚一下顺便装上 Python。然后 useradd -s /bin/bash -m -U paddleocr 创建个跑 paddleocr 的用户。su - paddleocr 切过去,python -m venv venv 创建虚拟环境,然后进去按 PaddleOCR 的文档装就行了。装好运行起来没问题之后,写个 for 循环把所有支持的语种都识别一遍,以下载各语言的模型(当然你也可以只下载你想要的)。做好之后可以清一下缓存啥的。gdu 就挺好用的。

哦,以上是 CPU 版本的安装流程。GPU 版本的可没有这么简单。首先要把显卡设备传进这个 nspawn 里。创建 /etc/systemd/nspawn/paddleocr.nspawn 文件,然后里边写上:





哦,这里有挂载 pacman 缓存目录前边忘了说,不过这个不重要啦。这里指定了用户,但是可以在命令行上用 -u root 覆盖的,不影响进去维护。私有网络,也就是给它配置个网络命名空间,里边除了 lo 外啥网络接口都没有。那它怎么访问网络呢?它访问不了网络啦。所以要 bind mount 进去一个 /run/paddleocr,用于通信的 UNIX 域套接字将会放在这里。网络不通,走文件系统就好啦。

然后找台机器把 AUR 包 cuda-10.2 和 cudnn7-cuda10.2 打一下,但是不用安装。我们不搞 CUDA 开发,里边有一大堆东西都是不需要的。把需要的库复制进 rootfs 里去就行了。至于需要什么库?进那个虚拟环境的 Python 里,import paddle 然后 paddle.utils.run_check() 跑一下就知道了。复制库之后记得跑 ldconfig 啊。

PaddleOCR 能跑起来之后,就可以把我的服务丢进去跑啦。最终命令长这样:

sudo systemd-nspawn -M paddleocr --user=paddleocr /home/paddleocr/paddleocr-http --loglevel=warn -j 4

-j 参数是限制并发识别数的,避免过载 CPU 或者 GPU,并不是线程数。

跑起来之后,sudo setfacl -m u:$USER:rwx /run/paddleocr/http.sock 给自己授权,然后 curl 一下试试:

time curl -sS -F file=@a.png -F lang=zh-Hans --unix-socket /run/paddleocr/http.sock http://localhost:5174/api | jq .

对于小图片的话挺快的,不到一秒就能出结果。我使用 CPU 版本跑的话,会慢个近十倍的样子。顺便说一下,这是我对服务进行性能优化之后的结果。之前每张图都开新进程跑太慢了。大概是需要加载一大堆库,然后把模型上传到 GPU,每张图一进程的话 GPU 版本反而会明显慢于 CPU 版本。代价是服务会一直占用大约 2G 内存,即使你并没有在用。

系统挂起到内存或者休眠到磁盘时,内存里的内容是被保留了,但是 GPU 显存并没有,大概因此会报 cuda runtime error 999。这时候,只需要停止服务,卸载 nvidia_uvm 内核模块然后重新加载,再启动服务就可以恢复了。如果 nvidia_uvm 卸载不掉的话,那就没办法了,要么重启,要么改用 CPU 版本。NVIDIA 是有个把显存 dump 到内存里存起来的方案的,但是没必要啊,尤其是休眠到磁盘上的时候,多浪费时间啊。


你可以直接用我做好的文件。通过本地的 IPFS 服务访问:



我把用于跑服务、设置权限的配置文件打了个 Arch 软件包。nspawn 用的 rootfs 也打包上传了。PaddleOCR CPU 和 GPU 版本是分开的,所以有两个包。CPU 版本的 nspawn 叫 paddleocr-cpu,服务名也是。把 rootfs 解压到正确的地方之后,systemctl start paddleocr 或者 paddleocr-cpu 就好啦。用户需要加入 paddleocr 组才能访问 HTTP 套接字哦。

如果遇到CUDA error(803), system has unsupported display driver / cuda driver combination报错,请将系统当前的 复制进 nspawn 里:

sudo cp /usr/lib/ /var/lib/machines/paddleocr/usr/local/lib

另外服务配置文件放到 GitHub 上了:paddleocr-service

kookxiang's avatar

使用 Jellyfin 搭建二次元媒体库

10 年前,叔叔开始购买版权,打开屑站就能随时随地同步追番。

10 年后,即使你“游泳”到港澳台地区,你也只能看到少量蜜汁和谐过的内容。


为什么用 Jellyfin

其实最开始垃圾瓢虫安利的是 Emby,但是这公司似乎是掉到钱眼里面了:

  • 在不同的设备上播放都需要内购
  • 下载文件到本地离线播放需要内购
  • 硬件解码也需要内购

就很离谱,不想每个平台都买?那你得订阅 Premiere(我现在还不能理解为何这点功能能卖 $54 一年)


Jellyfin 是 Emby 3.5.2 版本的一个开源 Fork,自由且免费。

Jellyfin seeks to continue development of the original Emby project with a Free Software ethos. It is committed to bringing all its users access to the best possible Media System, developed entirely by a community of volunteers who contribute code, documentation, translations, and support to the project.
About Jellyfin

Jellyfin 安装很简单,可以用 Docker,可以直接下载运行,而我的选择更简单——AUR
毕竟是 .NET 开发的软件,对运行环境没有依赖,直接交给 systemd 管理即可


由于 NAS 本身数据都在 ZFS 上,就直接开了一个 Jellyfin 的 Data Set,关闭了压缩和去重。


├── Series (2010)
│ ├── Season 00
│ │ ├── Some Special.mkv
│ │ ├── Episode S00E01.mkv
│ │ └── Episode S00E02.mkv
│ ├── Season 01
│ │ ├── Episode S01E01-E02.mkv
│ │ ├── Episode S01E03.mkv
│ │ └── Episode S01E04.mkv
│ └── Season 02
│ ├── Episode S02E01.mkv
│ └── Episode S02E02.mkv
└── Series (2018)
├── Episode S01E01.mkv
├── Episode S01E02.mkv
├── Episode S02E01-E02.mkv
└── Episode S02E03.mkv

这类个人推荐使用 Root > (Series) > (Season) > (Episode).xxx 的结构,可以降低元数据拉取的难度。

动漫的来源主要有 3 类:

  1. 新番订阅类,个人比较常用的是
  2. BT / 离线下载,主要是下载一些压制好的老番,甚至准备了一个专门运行这类奇怪下载器的服务 (限内网)
  3. 一部分 PT 站下载的内容

其中 1 2 比较简单,直接配置下载目录到媒体库中,或者下载后移动过来即可
3 的话略微麻烦一点,需要保留一份原始结构用于挂种,之前我用的 ZFS 自动去重,现在更推荐走软连接


Jellyfin 最大的问题是内置的几个数据源对动漫支持非常差,基本上只有像名侦探柯南这种才可能有元数据。
其实也还行,不过搜索的时候最好用日文名去搜,我现在是 Bangumi + TMDB 双持了

好在 C# 我也会一点,于是整了个 的数据源插件:

安装后在媒体库设置中勾选 Bangumi 这个数据源就能用上。

这个插件核心功能之一是自动识别集号,还记得官方的命名要求吗?文件名得有 [E01] [S01E01] 这样的内容才行。

实际上国内字幕组在命名的时候五花八门,有 番剧名 - 01,有 [番剧名][01]……
而且还会在文件名里带上奇怪的内容,比如 [1080P] [10bit] [h264] 还有部份文件名带了 CRC32 Hash 的。

直接带来的问题就是在默认情况下 Jellyfin 会识别成第 1080 集等奇怪的集数,一个个重命名过于麻烦,且对于 RSS 这类自动下载文件名的需要手动修改就更麻烦了。
所以插件内内置了几个常见的正则,涵盖了上面的几种情况,可以自动识别出正确的集号返回给 Jellyfin。

此外插件还会使用 bgm 上的评分和标签数据,这样界面上显示的评分和推荐视频会更科学。


搭建好 Jellyfin 以后就可以直接在网页版看了,不过受限于浏览器,能够硬解的内容实在少得可怜,还是更推荐使用客户端看。

另外为了提升服务端上转码效率,我给 NAS 插上了矿潮时斥巨资 ¥450 购买的半高 GeForce GTX 1050 Ti

装上 nvidia-dkms 以后重启服务器,后台启用 NVENC 转码即可

如果还想折腾,可以打下破解补丁,解除消费级显卡上的 NVENC 数量限制

MPV + Anime4K + SVP4

这里必须安利 jellyfin-mpv-shim 这个官方项目。

它的原理十分简单,在 Jellyfin 上选择投屏到它播放后,它就会自动调用 MPV 进行播放。
由于是走的投屏,可以使用手机登录 Jellyfin 进行远程控制,我这种床头吊显示器的用户表示爽翻了。

jellyfin-mpv-shim 自带的 MPV 已经内置了 Anime4K 的支持,按回车键进入菜单后选择对应的 Profile 即可。

MPV 也是能够用上 SVP 的插帧能力的,不过 jellyfin-mpv-shim 已经内置了支持,参考 readme 打开开关并配置使用 SVP 安装目录下的 MPV 就行了。

PS:这套方案我也在 macOS 上跑通了,但是 Macbook Pro 的性能…… 此外还要占用一个 SVP 授权,咕咕咕了


之所以这几个放在一起说,是因为 Infuse 时苹果生态中最好用的媒体库软件,我的 iPhone / iPad / Apple TV 都用它。
如果你看的内容不涉及 4K HDR 以上的话理论上是不需要订阅的,不过它是在我 Apple TV 上唯一能正常播放杜比全家桶 Demo 还非常流畅的。

使用非常简单:新增文件来源中点击最下面的添加,菜单中直接可以添加 Jellyfin。


访问我的媒体库主要有 3 种方式:

  1. 如果你能收到 路由,那么你可以内网直接访问
  2. 可以通过 Cloudflare 中继访问,需自备梯子
  3. 可以通过腾讯云服务器中转,目前只有 5M 带宽,可能会卡


RecursiveG's avatar

使用 ascii-xfr 传输 rz 程序

最近继续折腾一些嵌入式设备,需要通过 UART 传输一些文件。一般来说直接使用 rz/sz 即可,但不幸的是 rz applet 并没有编译进这个设备的 busybox。同时由于一些蛋疼的原因,我们也不能重新烧写设备的 rootfs 镜像或者是从网络安装,那么就只能自己想办法解决咯……

基本思路是自己静态编译一份 rz 程序,然后从串口发送给嵌入式设备,然后再利用这个 rz 接收 sz。然后就能双向发送文件了。

设备上至少需要有cat命令和base64 -d。如果没有base64可以试试openssl base64 -d。如果还是没有那这篇文章的方法就不适用于你了。另外在设备上最好还要有一个 gzip 之类的压缩程序,可以显著缩短初次传输时间,以及一个 checksum 程序用于检查。我这里用了xzmd5sum


编译 rz

你需要有一个能用的工具链,我的设备是 AArch64,直接使用了源里的 gcc。

# 下载代码
wget ''
# 解压
tar xaf lrzsz-0.12.20.tar.gz
cd lrzsz-0.12.20
# 静态编译
CFLAGS='-Oz -static' CC=aarch64-linux-gnu-gcc ./configure --target=arm64-linux


使用 ascii-xfr 发送 rz


# 接上文
cp src/lrz rz
# 进一步裁剪文件体积
aarch64-linux-gnu-strip --strip-all rz
# 计算 checksum 备用
md5sum rz
# 压缩
xz -9 rz
# 编码成 BASE64
base64 rz.xz > rz.xz.64
# 连接设备,记得使用你自己设备的波特率
picocom -b 1500000 -s 'ascii-xfr -sn -c 1' /dev/ttyUSB0
# 在设备中准备接收
wimpy_board$ cat > rz.xz.64
# 按 Ctrl-A Ctrl-S,然后输入要发送的文件的文件名
*** file: rz.xz.64
# 等待字符滚完,然后按 回车 + Ctrl-D 退出 cat
# 传输速度是 1000Bps,我最终需要传输 420 多 KB,需要 7 分钟。
# 传输完毕后在设备上还原可执行文件
wimpy_board$ base64 -d rz.xz.64 > rz.xz
wimpy_board$ xz -d rz.xz
wimpy_board$ chmod +x rz
# 最后比较一下 md5 值,并确认可以正常运行
wimpy_board$ md5sum rz
wimpy_board$ ./rz
# 连按 5 次 Ctrl-X 退出

使用 rz 接收 sz


# 按 Ctrl-A Ctrl-X 退出 picocom,然后再重新连接。
# 不带 -s 参数时 picocom 默认使用本机的 sz 发送文件。
picocom -b 1500000 /dev/ttyUSB0
wimpy_board$ ./rz
waiting to receive.**
# 按 Ctrl-A Ctrl-S,然后输入要发送的文件的文件名
*** file: src/lsz
$ sz -vv src/lsz
Sending: lsz
Bytes Sent:1173536 BPS:144649

Transfer complete

*** exit status: 0 ***
依云's avatar

使用 EasyEffects 调整 Bose 音箱的体验

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

最近到手一个「Bose SoundLink Mini 蓝牙扬声器 II-特别版」音箱,蓝牙名称「Bose Mini II SE SoundLink」。这家伙小巧、沉重,黑色版和我显示器的黑色支架也挺合得来的。然而音质上我遇到了一点问题。

就如同 Bose 产品页说的,它「低音浑厚」。效果就是,只要播放的声音有一点低音,它都给它放大到很明显,震动人心的同时也震动了我的桌面。这用来听强调低音的音乐应该非常有感觉。可是,我听的大部分歌曲都是女声呀。这就像纯净清澈的蓝天蒙上了一层雾霾。

我用白噪声、粉噪声和频率连续变化的正弦波测了一下,用 Spectroid 查看,发现这音箱会加强 100Hz 及 7kHz 附近的声音。所以我把播放的声音处理一下,降低这个地方的强度不就好了吗——嗯,我需要个均衡器。

我记得群里有人提到一个叫 PulseEffects 的软件,于是找了一下。它已经更名为 EasyEffects 啦,不过仅支持 PipeWire。而我还在使用 PulseAudio,于是先装上 pulseeffects-legacy 试了一下。效果十分不错,清澈的女声回来啦(还丢掉了笔记本扬声器所附带的金属感)。不过有点吃 CPU,即使不显示频谱图,也大约得消耗掉 10% 的 CPU。群友说 EasyEffects 的资源占用很小,于是我花了一些时间,切换到 PipeWire 上来啦。

切换起来其实不难,我主要是担心有功能不支持以及遇到 bug。pacman -Syu pipewire-pulse pipewire-alsa wireplumber easyeffects就好啦。然后把 PulseAudio 的服务停掉,PipeWire 的对应物开起来,就切换完毕了。PipeWire 的 PulseAudio 兼容性还不错,pavucontrol 用起来完全没有问题,甚至还解决了之前蓝牙编码器在连接之后从 SBC XQ 变回 SBC 的问题。EasyEffects 的 CPU 占用大约在 4%,低了不少。我关心的另一个问题是网络支持,但我发现这个 PipeWire 也兼容了,同样的命令pactl load-module module-native-protocol-tcp auth-ip-acl=对 PipeWire 也能用。

至于 bug 嘛,确实有一些。虚拟机里通过网络播放的时候,偶尔会卡一下。EasyEffects 有时候会需要重启。刚刚不知道为什么音箱明明是连接上的状态,但是就是没声音。重连之后才恢复。我大概会用一些天,如果不是很严重的话我就不切回去了。

哦对了,easyeffects --gapplication-service 这样启动 EasyEffects 就可以不显示图形界面了。但是依旧需要连接上 Wayland 或者 X11,所以需要安排在图形界面启动之后运行。我给 EasyEffects 写了个 systemd 服务,WantedBy 自己写的,然后在~/.xprofile的最后启动一下,就可以了。

至于其它系统,Windows 上可以使用 Equalizer APO,Android 上我使用的播放器 Poweramp 也有均衡器功能。而且这俩也是可以给指定的输出设备配置的(不过好像 Equalizer APO 只支持一组配置)。


SgDylan's avatar

二〇二二 𝄇


's avatar



饭后干了些有的没的,玩了会冬促的时候买的Pistol Whip —— 本来这种时间是给SDVX的,但是最近抽nemesis crew暴死,再加上现在月租不是固定1日开始,可以等blaster gate的新歌出了再加入,所以就暂时退订了。老实说现在没有PC限定解锁的歌,不加入也没啥损失了。退订是从日本时间的1日起生效,所以我2022年最后一次玩SDVX是在30号。说多了。Pistol Whip感觉上因为要经常躲,所以比Beat Saber更锻炼全身,而且对手腕也很友好。不过我打的时候还是手忙脚乱的,只能打打简单关卡。

打了一会儿之后,室友开始睡觉,而我就在这里打这篇文章了。手机放着 Kessoku Band,一遍播完之后从头开始,零点的时候在放专辑的第一首《青春Complex》。





SgDylan's avatar



Felix Yan's avatar

[Arch] systemd 时代的 NBD 客户端持久化配置方法

NBD 用于提供块设备给远程设备使用是一种非常简便、低成本的方法。然而,让 NBD 开始工作的方法在网上能找到很多,但是 NBD 客户端的配置持久化却很难搜到比较完整的资料。在参考了一些过时博客、manpage 等比较分散的资料之后,我总算是凭借蛛丝马迹找到了应当是正确的配置方法。

1、自动加载 nbd 内核模块

echo nbd > /etc/modules-load.d/nbd.conf

(虽然——我觉得这件事应该在 nbd 包里完成,因为上游不愿意默认提供的理由只是为了考虑 nbd 未被编译为内核模块的情况。)



nbd 服务器、连接选项等本来在 nbd-client 命令中配置的内容,应当被写到这个文件里。


nbd0 export0 persist

显而易见,分别对应设备名、服务器地址、服务器上配置的 export 名、其他选项。完整的介绍可以参考对应的 manpage

3、/etc/fstab 和 nbd@<设备名>.service


此处的设备名应当和 nbdtab 内配置的设备名相符,nbdtab 的配置由这个对应的服务应用。和其他网络设备一样,挂载点、挂载相关的配置应当设置在 /etc/fstab。

这里需要使用 x-systemd.requires 来声明对 systemd 服务的依赖关系。由于服务会被这个依赖关系自动唤起,不需要手动 enable 服务。

/dev/nbd0 /var/lib/archbuild btrfs defaults,x-systemd.requires=nbd@nbd0.service,_netdev,nofail 0 0

这里的 _netdev 会让 systemd 等待网络可用后再进行挂载。注意我在这里写了 nofail 以避免因为网络原因使设备无法启动。如果要考虑在访问时再挂载,也可以改用 noauto,x-systemd.automount 之类的方法。


The post [Arch] systemd 时代的 NBD 客户端持久化配置方法 first appeared on Felix's Blog.
Felix Yan's avatar

[Arch] OpenSSL 3 更新杂记

最近 Arch Linux 终于把 OpenSSL 更新到了 3.x 系列版本。一直以来,在处理涉及打包工具链本身的 soname bump 等更新问题时,我们一直缺乏一个透明、优雅的流程。

以往采用过的方法包括但不限于:临时往编译环境里手动塞旧版本兼容包、手动在过渡版本的新版 PKGBUILD 里再编译一份旧版包然后把 lib 装进去等。由于处理这件事的开发者一般独自完成了整个过程,留下来的资料除了 IRC 里的寥寥几语往往十分有限,对于其他开发者、或是下游发行版试图重现这个过程来说,都是一个比较痛苦的过程。

这一次趁着 OpenSSL 3 的机会,本喵深度参与了整个 bootstrap rebuild 过程,并且在 RISC-V port 里复现了一遍。现在记录一下大致的过程和遇到的问题,以备不时之需。



由于需要避免文件冲突,相应的编译选项(–libdir)和 package() 过程中做了一些兼容性处理。如果这个包还需要在 rebuild 之后留下来,比如这次的 openssl-1.1 的情况,头文件和 pkgconfig 的 .pc 文件也需要做处理。如果只是作为兼容包,可以仅保留带 soname 和具体版本的库文件本身。(当然,这种情况下也可以考虑在新版包里直接编译一份旧版库安装进去,毕竟只是临时使用。


这里用到的技巧是,在 package() 方法内追加 depends,以避免编译环境中提前引入这个依赖,产生文件冲突(此时的仓库中,原包名对应的包仍然是旧版本,和 openssl-1.1 兼容包存在文件冲突)。

3、用此时的环境 rebuild 整个工具链需要用到的基础包

这一步具体要处理哪些包需要仔细分析。以这次 openssl rebuild 为例,一共有这么多包需要先使用这个依赖了旧版库的新版包来 bootstrap 以避免破坏打包环境本身:

libfido2 (for systemd's tests)
tpm2-tss (for systemd's tests)

4、rebuild 新版库,去掉对旧版库的依赖

在完成这个步骤之后,需要重建 build chroot 以去掉之前引入的旧版库(这确实是 Arch 工具链的一个毛病,当前的机制不能自动保证 chroot 是干净、完整的)。

5、将前面步骤里 rebuild 过的基础包再次 rebuild


至此,已经可以继续平常的 rebuild 过程处理剩下的包了。

OpenSSL 更新进入稳定仓库后,在群组里果然遇到了大胆部分更新的用户不幸炸掉自己的 pacman。甚至有使用 pacman-static 的用户也因为 sudo 链接到 openssl 而悲剧了。再次奉劝大家切忌对官方仓库内的软件包部分更新,尤其是这种一看就不好惹的系统核心库!

另外,本喵自己的老 OpenVPN 配置在更新后也被制裁了:

SSL_CTX_use_certificate:ca md too weak

于是只好加入了 tls-cipher "DEFAULT:@SECLEVEL=0" 从此远离了推荐的配置

The post [Arch] OpenSSL 3 更新杂记 first appeared on Felix's Blog.
's avatar

USTC Hackergame 2022 - Writeup

USTC Hackergame 2022 - Writeup
Felix Yan's avatar

用 pacman-accel 给 pacman 加速


比较容易想到的解决思路是:只从同步延迟低的镜像下 db,然后从速度快的镜像开始依次试,跳过 404 的镜像,直到找到一个已经存在该文件的镜像。

在过往的十来年里,我一直是通过写一个脚本来分别给 pacman -Sy 和 pacman -Su 设置不同的镜像来勉强解决的。但是这个用法在 pacman 最新系列中被破坏了——pacman 加入了一个镜像站如果 404 次数过多,在同一次更新中就再也不尝试了的新行为。

想到以往的用法会在命令中夹杂许多 404 报错,需要专门的脚本来换镜像体验也并不是很好,我写了个非常简单的本地服务来实现这个需求:

# A simple local redirector for pacman, to get you the latest packages and
# utilize available mirrors.
# Usage:
# - Set multiple mirrors in /etc/pacman.d/mirrorlist-accel with ordering:
# https://fastest-mirror-but-updates-once-a-day/archlinux/
# https://relatively-slower-mirror-that-updates-more-frequently/archlinux/
# ...
# https://pkgbuild-dot-com-or-another-mirror-that-gives-you-the-latest-packages/
# - Set /etc/pacman.d/mirrorlist to this redirector:
# Server =$repo/os/$arch

require 'http'
require 'sinatra'

mirrors = File.readlines("/etc/pacman.d/mirrorlist-accel").filter_map { |line| line.strip if line && line[0] != "#" }

get '/*' do |path|
    # Set TIER 0/1 mirrors as the last one, for:
    #  - DB syncing
    #  - Download fallback
    # These two use cases always the same server for consistency.
    mirror = mirrors[-1]

    unless path.end_with? '.db'
        # Find a faster mirror with the requested file present
        mirrors[..-2].each { |m|
            response = HTTP.head(m + path)
            if response.status == 200
                mirror = m
       "skipping #{m} for #{path}, code: #{response.status}"
    end "redirecting to #{mirror + path}"
    redirect mirror + path, 302

set :bind, ENV.fetch("PACMAN_ACCEL_BIND", "")
set :port, ENV.fetch("PACMAN_ACCEL_PORT", "4567")

如注释所说,在 /etc/pacman.d/mirrorlist-accel 里按照本地访问速度依次设置几个快的镜像,并把最后一个镜像设置为和上游同步频繁的镜像即可。


$ cat /etc/pacman.d/mirrorlist-accel

$ cat /etc/pacman.d/mirrorlist
Server =$repo/os/$arch

原理很简单:pacman 访问本地 HTTP 服务,这个服务对非 .db 的下载请求按照配置依次 HTTP HEAD 直到找到一个返回值为 200 的镜像,然后返回 302 让 pacman 从这个镜像下载。

由于 .db 文件全部由最后一个镜像提供,而前面配置的镜像不存在对应文件时最终也会 fallback 到最后一个镜像,这样使用应该不会产生新的 db 不一致问题。

这个简单的工具日后会在我的小工具集 GitHub 仓库继续维护,同时我也创建了一个简单的 AUR 包:

The post 用 pacman-accel 给 pacman 加速 first appeared on Felix's Blog.
SgDylan's avatar



's avatar


因为Notion的字体看起来很不爽(黑体宋体夹杂),就在word上写了。在word上用的是霞鹜文楷。想不起来名字的时候就会想起来原神的可莉,这个字体的原型是名叫可莉的字体。二来word也有所谓的focus mode,会隐去一切无用UI。windows更新以来也有了focus session。可以做到一种二重buff。在加上平时会开的focus assist,就可以聚焦到烧焦了!火,生命之火。燃烧,生命的意义。果然只有燃烧的时候,蜡烛才在证明自己的存在。SM?不SM。


第一部看的是《Nuovo Cinema Paradiso》(天堂电影院)。看的原因的话,果然还是《我想成为你的眼泪》(君の涙になりたい)这部小说。书中提到的是电影最后的片段,再者好像是名作,于是就看了。我也是按照书里的推荐,没有看3小时的版本(导剪版,反而越剪越长,真是疑惑),看的127分钟的原版。……是127分钟吧。讲的是一个人随电影成长的故事,然而电影没有伴随着人成长,而是默无声息地消失了。至于问题的场景——或者说问题的场景的剪辑——如果那些电影我都有看过就好了,我是这么想的。如果没有看过,就会像我现在这样,试图从干燥的海绵上硬挤出一两滴水。如果看过的话,就会像主人公那样,既失笑又怀念,笑着笑着不禁眼泪流下来吧。所以可能还是生错时代了。

第二部是《映画大好きポンポさん》(最爱电影的庞波小姐,或者是别的称呼)。讲的是围绕新电影制作,新人导演Gene Fini,新人女演员Natalie Woodward等人如何活跃的故事。一个特点,就是将电影制作,从一开始的剧本、选人,到最后的剪辑都有涉及。所以论meta程度,这部应该是最深的了。虽然Gene想看《天堂电影院》,但是Pompo小姐显然不是很有耐心看完,甚至暴言道,超过90分钟的电影都没意思。而这也是这部电影有意思的一个地方。因为涉及剧透,我就不想提及了,但是这种meta发言也是让我眼前一亮的地方。最近在看的《ユア・フォルマ》(Your Forma,中文不知道)(我讨厌word的地方:日语输入法切换 Alt+`,会调出听写)里,插图里有很多QR码,按文中的说法,是电子毒品的信息,不能直视,因为会脑内自动扫描。如果真的用手机扫了,其中一个QR码还会说“明明告诉你不要看了”。

第三部,其实不是第三部。是《Bullet Train》(子弹列车),大概1小时40分钟。听同学说的于是打算看,正好发现附近的AMC有上映,于是就去看了。说是附近,骑车也要40分钟。回想起来,还是不清楚这是好事还是坏事。去程是坐公交去的,这里的公交,前面有自行车架,可以放两辆车。因为去的时候是中午(中午的电影能打7折,加起来只要10刀),于是开映前在附近的Hyvee吃了顿饭。不知为什么少算了4刀左右,告诉了员工,员工也不在意。到场之后发现虽然场子很小,但是根本没有人来,只是之后来了两三人,四舍五入就是包场。内容,就是一般的好莱坞动作片,没有字幕所以有时候会听不懂。虽然之后也有枪林弹雨,但是子弹列车字面上指的是新干线。讲的是一群特工在新干线上从东京打到京都,最后死了几名特工,整列车都被整脱轨了。不仅仅是三方一两损的程度了。大家都亏大了。回来的路上才发现,自行车的前灯没了,只好重新买了一个。所以,到底是值不值呢。一说,是经验千金难买,但是问我想不想再经验一次,我大概也很难给出个肯定答案了。


说了这么多之后,第四部。是《Singin’ in the Rain》(雨中曲)。这是我分享了看完《子弹列车》之后,在频道里看到别的人最近看的作品,心血来潮在亚马逊上租了,当晚(说是当晚,其实是昨晚)麻将打了一半走人回家看的。大概也是1小时40分钟。嬉笑舞蹈之间讲述了电影业从无声到有声的迅速变迁。《La La Land》也是很久以前看的了,所以这种音乐剧特有的,二话不说跳起舞来的风格,让我首先想起的是印度的电影。可没想到上世纪的好莱坞就有了。我看到“call me a cab”“hey cab”这样的冷笑话也会禁不住笑起来,所以整篇看完非常愉快。挺喜欢男二的Cosmo,尤其是他的《Make ‘Em Laugh》。名作,也不一定就是深沉的,大概吧。


SgDylan's avatar

全新 NAS 安装简单记录

老 NAS 到手已经已有五六年,错误频发加上 ARMv5 的性能已经不能满足需求。
于是咬咬牙自行组装了一台全新(并不是)的 NAS,大概记录一下遇到的问题。

SgDylan's avatar


Basic Authentication 成为咱自建服务的主要鉴权方式已有快十个年头了。

Roy Binux's avatar


上一篇中,我们实现了屏幕触控的物理输出,但是钓鱼这个小游戏还是需要根据画面反馈来做动作的。我一开始的想法是用一个摄像头拍摄平板的画面然后进行图像处理。尝试了一会发现,就摄像头这 720P 的分辨率,光是梯形校正准确率都不高,可能做个图像分类还行,但是要分辨画面中的信息对我来说还是有些困难。然后睡前一阵查找,发现了软件实现的 AirPlay。


AirPlay 是 Apple 的屏幕镜像和投影协议,这个协议已经被逆向并且有软件实现了,例如 LetsView, AirServer。通过 AirPlay + 接收软件,我们可以将 iPad 画面镜像投影到电脑上,分辨率更高,而且没有色差,处理起来就更简单了。如果是安卓的平板,也可以通过 Chromecast 或者 Miracast 协议投影,原理是一样的。

当图像投影到 PC 上之后,就可以通过截取 PC 屏幕窗口的的方式获取到平板画面内容。第一种方案是使用 MSS,一个跨平台的 Python 截图包,它能以大约 20 FPS 的速度捕获图片。使用也很简单,首先需要获取投影软件的窗口位置和大小,然后按帧截图发送给 OpenCV 处理。实现的代码在这里。由于 MSS 并没有提供获取窗口大小的方法,它的区域捕获仅仅依靠的是屏幕坐标。所以获取窗口还是需要我们自己实现的,而这部分不是跨平台的,也没办法获知窗口移动。再加上 MSS 仅仅是截图,当窗口在后台时就失效了,使用起来并不方便。

而更好的方法是通过 OBS + 虚拟摄像头 + OpenCV,对的,就是平时游戏主播使用的直播软件。简单来说就是使用 OBS 捕获投影软件窗口,再通过虚拟摄像头输出给 OpenCV。OBS 软件本身是跨平台的(但是在不同平台可能会有些不同),FPS 要多少有多少,而且窗口可以被遮挡(窗口不能最小化到后台,但是可以放在另一个 Virtual Desktop),窗口移动什么的也完全没有问题,专业的确实就是专业。OBS 设置部分很简单,只要增加一个 Source,然后再根据需要调整输出分辨率就好了。Python 部分的源代码在

Note:虽然最新的 OBS 自带了 Virtual Cam,但是似乎在 Windows 上和 OpenCV 有兼容性问题,捕获的画面是黑的,依旧需要使用插件解决。



  1. 很多游戏操作是有网络交互的,当点击按钮之后,会有不定长的延迟进入下一个界面,在下一步操作前进行画面分类识别能更鲁棒
  2. 在有的界面中,存在需要进一步识别的交互内容,例如钓鱼小游戏的浮标。先识别界面类型,能更有效和准确地决定是否需要进行这些信息提取。
  3. 获取当前状态可以使 bot 更灵活,脚本可以从任意状态启动。这一部分会再 第四篇 blog 中讲到。

在这个项目中,我直接抄了 tensorflowImage Classification Tutorial。对于这种标准的 UI 界面,随便什么模型效果应该都不差:classifier_training.ipynb

做图片分类的第一步是采集训练样本,你会注意到 screen_capture.pyobs_capture.py__main__ 部分都有 cv.imwrite 以及对应的按键绑定的代码。我首先会在开启图像采集的过程中,游玩游戏,手动进行需要自动化的整个流程,手动或者每 1 秒地频率采集一些原始图像。然后对应每一个分类新建一个文件夹,例如在《猫之城》中,我有 fish_idle, fish_ring, fish_drag, fish_reward 和 not_supported 这样一些分类。然后将采集到的图片拖到对应分类的文件夹中,我对于分类和图片的选取是这样的:

  • 分类之间的图像需要有较大的不同。例如,点击之后显示的确认对话框就没必要单独建立一个分类。
  • 每个分类选取至少 10-20 张 不同 的图片,尽量涵盖这个类别可能出现的所有变种,例如《猫之城》中 fish_idle 会出现不同的场景导致背景不一样。
  • 单个类别不应该有远多于别的类别的样本,最多和最少之间不超过 10 倍这样。
  • not_supported 可以用来放一些脚本用不到的 UI 截图来增加类别之间的差异性,以及在进入没有分类的页面的时候不会错误激活脚本。

然后就是套代码了,图片分类并不需要很高的图片分辨率,这里我随便选了一个 220x300 来保持图片宽高比,套示例模型就能达到 99% 的准确度了。因为是 UI 界面,也不存在裁切变换,之后实际测试结果也非常好。最后将分类列表和模型保存下来就可以啦。为了保存单个文件,并且减少体积,使用的是 TensorFlow Lite 模型,predict 的代码在。唯一需要注意的是使用的时候需要自己 resize 到 220x300,并且 OpenCV 图片的颜色是 BGR 而 tensorflow 是 RGB 的,需要要进行转换。其他就没什么了,总共有效代码也就 15 行,踩着巨人的肩膀,使用成熟的库之后还是挺简单的。

Note: 图片中的 fish_ring = 099% 就是图片分类的结果和 score,而其他的图片识别内容和辅助线就在下一篇 blog 中讲解啦。

依云's avatar


本文来自依云'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" &

# 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" &

# 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/ "$1" "$file"

kill $child_pid

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

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

Description=PaddleOCR HTTP service

ExecStart=/home/lilydjwg/PaddleOCR/paddleocr-http --loglevel=warn -j 2



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

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

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

Roy Binux's avatar


真的有2年半没有写 blog 了。我是那种不愿意在事情尘埃落定之前,把它写下来的类型。在这两年半里,H1B 抽到了,也跳槽了。收入上去之后,也更愿意花钱解决问题,而不是自己做点什么,有好几次想要提笔,又感觉没什么好写的。以后会改善吗?我觉得不会,虽然我依旧会去尝试各种新的东

依云's avatar


本文来自依云'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 很吵,也会浪费能源、加剧气候变化。

依云's avatar

tmux 状态栏优化

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

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


这些时间戳哪个先更新、哪个后更新可完全说不准的,你可能看到明明在地球另一边的服务器上先到某一秒,本地才跟上。甚至同一个 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 自己已经把时间对齐到更新间隔了。

依云's avatar

Google Chrome 中的字体设置

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

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

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

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


实际上它们分别是「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 或其变种,所以制作网页上还是得手动指定一些现代点的字体。

RecursiveG's avatar

折腾 RISC-V 单片机 Part1

大约半年前在 SparkFun 上买了一块 RED-V 开发板。基本算是 HiFive1 Rev B 的克隆,比 Rev B 便宜一些,同样使用 SiFive 的 FE310-G002 处理器。SiFive 的 MCU 自带一套 SDK,不过要搭配指定的 IDE 才好用,作为有洁癖的 Linux 用户这当然是不行的,所以就有了这次的折腾。


我一开始是打算用 SDK 的代码,不过看了一圈感觉这样和玩 Arduino 也没区别了,失去了折腾的意义,所以决定放弃 SDK 直接用汇编艹寄存器。

  • SparkFun RED-V Schematic: 这是板子的电路图,用来看板子上的接口都具体连接到 MCU 的哪个针脚。
  • Freedom E310-G002 Datasheet/Manual: 这两份文件可以从 SiFive 的网站上下载到,包含几乎所有重要信息。
  • Clang: 编译器。为啥不用 GCC?因为我不想单独折腾一份 GCC 的工具链。Clang 既可以编译出 x86 程序又可以编译出 RISC-V 程序,从软件源里装好就可以直接用了。
  • LLD: LLVM Linker。这下也不用配置 RISC-V 的 binutils 了
  • OpenOCD: 负责和板子通信,烧录程序,调试等。

三份 PDF 文档需要自己下载,而其他三个程序应该能直接用包管理装。


简单而言,单片机不像 CPU,有各种内存保护/分页机制等。所有的硬件控制/非易失存储(硬盘)/易失存储(内存)都位于同一个 Memory space。
详细信息可以在 Manual Chapter 4 中看到,比如说,[0x20000, 0x21FFF] 就属于 “OTP Memory Region”。

另外一些地址可以用于控制硬件,比如[0x1001_2000, 0x1001_2FFF]属于 GPIO,稍后可以看到,如果往某个特定的地址写1,那么芯片上的某个针脚的电平(电压)就会发生变化,如果这个针脚上连了一个 LED,那么它的亮灭状态也会发生变化。这些可以控制硬件行为的地址我一般称之为寄存器,注意不要和汇编语言里的寄存器搞混了。


一个重要的问题是,单片机在启动的时候到底会执行哪条指令?查看 Manual Chapter 5.1 可知:

On power-on, the core's reset vector is 0x1004.

即会首先执行 0x1004 处的指令(练习:请翻阅 Memory Map 查看该地址属于哪片区域?)并且手册也列出了预先烧录在该地址的指令列表。不过很可惜我并没有找到文档里所说的 MSEL pin 在哪里,不过从调试结果来看,0x1018处的jr指令最终会跳转到0x10000处。

继续阅读 Manual 5.1.1, 0x10000处的指令会跳转到0x20000,继续阅读Datasheet Chapter 5 “Boot code”,我们最终会跳转到地址0x20000000,查看memory map,该地址属于QSPI 0 Flash,也就是非易失存储器,看上去这里就是我们应该写入代码的地方了。(PC程序员初次看到Program counter能直接指向外部存储设备实在是刷新三观)。


向单片机写入程序的过程被称作 program(编程),所以用来编程的硬件也就叫做“编程器”了。还有一个我无论如何都无法理解的翻译叫做“仿真器”,英文叫 emulator, 这应该也是一个上古词汇,但是比起“编程器”,这个词更强调你可以调试单片机上的程序,比如下断点,单步执行等。现在一般用来指链接单片机和电脑的那根线。总而言之,你需要一个东西把单片机和电脑连起来,以便两者通信。

一般来说,这个硬件一头用JTAG等方式连接到单片机,另一头通过USB协议连接电脑,OpenOCD则是一个开源的程序允许你通过这个“编程器”以各种方式操纵各种单片机。当然也包括向 SPI Flash 中写入我们的程序。

在 RED-V 上,最大的芯片是一块 MK22FN128 的ARM单片机,这块单片机里预先写入了一个 J-Link OB 的商业程序,这个程序让这个ARM单片机实现了编程器的功能,所以我们只要直接用普通的USB线连接板子和电脑就可以了。J-Link 使用一种特殊的协议与电脑通信,好在Segger公司公开了这种协议的spec,所以OpenOCD也能和编程器通信啦。


  • 单片机通电后会经过一系列跳转,最终开始执行 0x20000000 处的指令。
  • 该地址实际访问的是外部的存储器。
  • 可以通过编程器向该存储器中写入数据。

To be continued in part 2.

's avatar






















Ratchet & Clank, the game和rift apart。这个两纯粹是老少皆宜的有趣游戏。




Phoenix Nemo's avatar

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


Felix Yan's avatar

萌新的 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:

这份文档可能已经颇为过时,里面编译 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 的安装会有多坑,可以参考这位受害者的体验:

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




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

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

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

The Rise and Demise of Twitter 2

This is written to memorize the discord channel Twitter 2.

Some Random Ranting

Not for everyone,

imi415's avatar

LPC MCUs and ELF manipulation

01. LPCs and vector checksum

After playing with NXP LPC series MCUs for some while, something has bothered me: Verification of program flash against ELF files always fail.

Verification failure

Hmmm.. That's strange. Tried a few different LPC models, and the failure position is always 0x1C. After digging into the user manual, I found out these MCUs uses the reserved 0x1C vector as a checksum for the first 8 vectors. The bootloader sums up the first 8 words, and if the result is zero, the flash content is considered as valid and bootloader will jump to the application reset vector.

LPC54102 UM

Different IDEs and tools reacts differently to this hardware feature, for example, IAR will compute this checksum by the linker during link stage, and the final image or hex files will contain correct contents. J-Link and OpenOCD process this checksum beforee flashing the image to target MCUs, regardless what the original image has.

However, this checksum machnism causes a problem when verifying the flash content against original ELF files, since the original file still contains invalid value for the checksum field, which will incremential download the first flash sector even if the application has no changes.

02. ELF Patching

Since I already know how the checksum is computed, the next thing is to find a way patching the ELF image, writing the correct checksum into the specified offset in interrupt vector table. Instead of grepping and using some regex magic, I decided using libelf from elfutils to manipulating the section data.

The documentation for libelf is too brief to give me the instructions on how to modify the content of a specific section. After several hours of searching between mailing lists and with the help of an old book, I finally get the library working:

((uint32_t *)ivt_data->d_buf)[7] = checksum;

It is important to use MMAP commands, which will modify the data in-place without touching other section information (Since we only modify one word).

Elf *e_handle = elf_begin(elf_fd, ELF_C_RDWR_MMAP, NULL);

The ELF will be modified in-place, which can be successfully verified by J-Flash.

The code is published at GitHub.

Patch OperationSuccessfully Verified

03. References



[3]: (Sign in required)

imi415's avatar

FreeRTOS on CH32V307

Porting and running mainline FreeRTOS on WCH CH32V307 RISC-V MCU
's avatar

Intrigued by Randomness: Mahjong, Crappy Machine Learning, and Gatcha Games

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.

Humans are pretty drawn to randomness. That’s probably a well-known fact, but I’ve just come to understand on how true it is, and I want to talk about it with regards to some of my personal experiences, as presented in the title.

Mahjong is a complex game. I’ve seen elders gather in community space and play it, but never had any idea why the tiles held by winning players make them win. There are more interesting games to play though, so I didn’t try to learn it. That is, until the online game Mahjong Soul came out, which is just Japanese Mahjong with random players online. But the game is obviously targeted to my generation, with cutesy artstyle and player avatars being anime girls (they even added anime guys later). So lots of my friends started playing it, and pretty enthusiastic, too.[1]

A game in Mahjong Soul

I tried to dabble in it, but the new player guide is filled with undecipherable jargon, unknown phrases explained by even more unknown phrases, 20+ different winning conditions, all intertwined. The rules overwhelmed me to no end, so I gave up. Until this year, when many of friends are still playing it often, they instigated me to play again. I said I can’t understand the rules, threw them bags of questions like "what does phrase X even mean?", in attempt to show them my inability to understand the rules. But somehow, this time, they actually had the patience to answer all of my petty questions. Over an hour later, being the one asking questions, I inevitably started to grasp the rules of Mahjong, or at least, quote one friend, "about 20% of Mahjong rules".

Wait, what, there’s more? That took such a long time and so much patience out of everyone, and I only learned about 20%? I exclaimed, "That just shows how complex Mahjong’s rules are!"

They suggested me to "just dive in, understanding will come naturally". But my experience disagrees. All I can do is play robotically or arbitrarily. There is no time in each turn to think everything through, most of the time I have no idea what I am doing. There is little feedback otherthan winning. If I take some action, it is very difficult to know whether that action is beneficial or harmful. When I lose, even if every action taken in that game are good decisions, I wouldn’t know. If I do win, what does that mean? Which decisions have lead to the win?

Comparied to other tabletop games like chess, Mahjong is much more complex. It requires such a high commitment, one needs to sit down and study it, just to be able to start playing. Whereas in chess, learning the rules is quick and feedbacks are easy to come by during plays. A piece is taken, that likely comes from a previous wrong move; I put the opponent in a difficult position, that probably means I did something right. To me, chess is a superior game than Mahjong.

But I missed a critical factor: Chess has zero randomness[2], it’s a game of pure skill (or in my case, at least blind luck). Mahjong is over halfly determined by chance, the starting hand is random, each tile is drawn from a shuffled deck. Skills do matter, but each player is still at the mercy of Fortuna. Better players are merely "more likely" to win, which only show up in a large number of games. In an isolated set of several games, worse players still win sometimes. This means, as some other friends pointed out, Mahjong is a great party game, the fun comes from the randomness. Indeed, if all that happens is weak players being crushed by strong players, it wouldn’t be fun for a group of people who have different skill levels. That happens with chess, and all other games that emphisize skills.

The luck factor also brings thrills to experienced players. Something unpredicted can always happen, anyone may have drawn a tile they need at any time, or not having drawn a useful tile for a long time. There can always be something new. I suppose that is why the elders in community space play it from day to night, seemingly tireless.

A couple of years ago (I can’t believe it has been that long), I wrote a chatbot, HoroBot. It runs in a couple of Telegram and IRC groups. It’s core functionality is to send random emojis whenever discussion is happending in the group. It is just for fun, seeing how merely sending random emojis can make a bot seem to blend in a group chat. This was a success, some didn’t even notice that it’s a bot hanging out amongus, for a long while. They joked that it has passed the Turing test. Later, I added more functionalities to it, one of which is detecting whether a chat participant is "of the bot’s own kind". Because Horo is a wolf, this feature is called the "wolf detector".

The way the wold detector works is based on a Bayesian classifier. Don’t worry, you don’t need to know anything technical. And neither did I. All I knew was that it’s a tool that lets a computer program to categorize sentences automatically. First, train the program with a set of sentences and what category each sentence is known to belong, then, with enough training, the program should know what a sentence in each category "looks like", so that when I give it another sentence, it will tell me its category. It’s a form of machine learning. Such things is often used for detecting spam messages, or whether user reviews to products is positive or negative. I wasn’t using it for anything that serious though, I wanted HoroBot to tell whether a chat participant is "of HoroBot’s own kind" or not.

Graphical explanation of this type of machine learning. Source: Yan, Ma et. al. CC-BY

For my application, there are 2 categories, "is" and "is not" (of HoroBot’s own kind). I trained the program with past chat log of the ArchLinuxCN group at the time. Messages sent by anyone with name containing "horo" is considered to be in the "is" category, otherwise "is not". It was obviously pointless, but the whole thing was just for fun. After training, whenever HoroBot sees another message, it will give a category of that message, and the categorization given to the last 100 messages sent by one user determines their score, i.e. "probability of being the bot’s own kind".

Someone (top) using the wolf detector. HoroBot (bottom) replies with "(redacted) has a 56% change of being of my kind."

The results are, as expected, pretty useless. What does even mean to be "of HoroBot’s own kind"? And because there is no way of interpreting this score, there is no way of telling how accurate it is. It appears that most of the results are around 50%, which gives the suspicion that if I put everyone’s scores up, it’s going to be a normal distribution, i.e. the category of each message is pretty much random. I have not tested that suspicion, though. Actually, it’s potentially worse than random guesses, the "original horos" (people who have names containing "horo" in the chat logs I used for training) tried to see their score, and they were consistently lower than others!

I will confess now, that I do have some idea why my Bayesian classifier was so crappy. A Bayesian classifer needs a sentence to be a list of words, rather than a list of characters. I need a way of segmenting the sentences into words. In Chinese, there are no spaces between words, so segmenting is not trivial. I wasn’t bothered to to anything elabourate, so I just decided arbitrarily: every 2 characters is a word! That is clearly a terrible way of doing segmenting. It has probably made every sentence meaningless.

Incredibly, despite all that, this is the most popular feature of HoroBot! To this day, several years later, when I have not updated the bot in a long time, participants of ArchLinuxCN chat group still seem to see their wolf detection scores often. It puzzles me: It’s just a pointless, practically random number! Why have they not gotten tired of it yet! But well, I guess, I made something that people liked, it should be something to be pround of.

This is purely baseless speculation, but I guess one reason of the wolf detector being popular is it’s unpredictability. The result is not random, the same message always yields the same categorization, but over different messages, it appears random. Yet we know the score is somehow related to the messages a person has sent, while that relation is unknown. Such randomness intrigues us, as we are curious to see "Does sending this message make my score go up or down?" Psychologists say that humans loves finding patterns in random events. Here we do have patterns, how unsensible it may be, it’s somewhere within the algorithm, and knowing this makes us wonder all the more strongly.

I’d like to think of myself as a relatively rational person. Playing gatcha games is a thing to be looked down upon in my book. Here, gatcha games refer to video games that has subpar gameplay, and the main attraction is collecting "cards" which are obtained randomly upon paying microtransactions. The cards are usually depicted as lovable characters.

The profits of gatcha game makers is surely betted on our attraction towards randomness. Each time one pays for the microtransaction, they can be intrigued by the anticipation of their new card (or lack thereof, most of time in this type of games, getting a collectible card is a rare event, usually they are less exciting stuff). The problem is, being attracted to that means I become more likely to spend more money on the totally useless cards, and devote time to virtual characters who have absolutely no care for me (other than their creators wanting to extort for more money). This is different to being drawn towards playing Mahjong with friends, which at least practices friendship; or watching over a wolf detector score, which doesn’t cost much.

I would argue that playing gatcha games is a lot like drinking alcohol. Doing it a lot is definetely bad. Doing it a little is probably acceptable, but unhealthy nonetheless. To be fair, it is really difficult to resist paying for the card draws, so I resorted to just stay away from gatcha games. That resulted me in missing out on some games with gatcha mechanics but do have some content, unfortunately. 🐁

  1. One became so devoted in Mahjong, he even went ahead and made an actual AI for it. It was just published recently.
  2. Other than maybe using some random means of determining who gets to move first.

Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

kookxiang's avatar


也许你会好奇,为什么在 2018-08-14 后我的博客就再也没有更新了。

呃,这个事情怎么解释呢… 也不是故意要咕…

主要吧… 工作这种东西就很忙…




用 Hexo 的目标也非常简单,希望新的博客能专注于内容,不整那些花里胡哨的东西

不知道有没有人看,剩下的内容就吐槽下之前考虑使用的博客系统 锐评各大博客系统


老博客用的就是 Typecho


  • 还算轻量?
  • 文章默认用的 markdown
  • 之前弄了一些小插件(虽然有些已经挂了)


  • 开发组比我还能咕(今年突然宣布诈尸了,也不知道为啥)
  • 用的人太少,自然也没什么人去做主题插件
  • 有一说一,现在感觉服务器上搭 php 环境不如其他语言简单方便(要配挺多东西)


同属 PHP 系的博客系统,应该是目前用户量最大的


  • 完善的第三方主题/插件,甚至有成熟的付费和定制化市场
  • 像我这种套 cloudflare 的还有对应的优化插件
  • 持续维护中


  • 没找到合适的主题,主题市场热销大多是把 WordPress 改造成商城之类的,要不就是列表强依赖图片,像我这种不配图的文章就特别丑
  • 老生常谈的性能问题
  • 哪怕我不用 WordPress,nginx log 里面都一堆 wp-login.php 和 wp-admin 的请求,需要保持更新


基于 node.js 的博客系统


  • 默认主题颜值还行
  • 所见即所得编辑器
  • AMP 支持
  • 对第三方模块嵌入支持很好


  • 默认主题虽然好看,感觉还是需要文章配图才能看
  • 几乎没找到啥第三方插件,官方甚至叫 integrations 而不是 plugins


Medium 是我看到最理想的博客系统了,其实我原本是打算直接用它的,甚至里面草稿箱还有半篇文章


  • 所见即所得的编辑器
  • 阅读页设计单纯,对于文字和代码的展示都不错
  • 有官方持续维护,不需要自己维护
  • 文章阅读可以带来收入
  • 可以绑定自定义域名(需要开会员)


  • 墙了
  • 主题能够自定义的内容很少
  • 付费制度,免费用户能看的文章有限,不知道还会不会有人看
  • 英文网站,如果写中文默认字体丑出翔(直接劝退)


嗯… 公司弄的东西…
其实准确的说是 Alipay Inc. 的东西


  • 编辑器简单好用
  • 界面清爽好看


  • 不能自定义域名挂在自己网站(虽然可以自己反代)
  • 完全没有自定义主题/插件能力
  • 因为公司在用总有一种在加班的感觉(




  • 静态化部署,可以直接丢 OSS 或者 CDN 上面,理论性能最快
  • 相对还算丰富的第三方插件
  • 直接解析 markdown 文章,方便导入也方便导出


  • 官网的第三方主题很多粗制滥造的主题(并不是想喷这些作者,只是觉得官方需要筛选一些好的放在自己的页面上)
  • 奇怪的模板引擎,虽然可能只是我没见识过

嗯,最后选了 Hexo 先用着,如果后续有更好的选择也能方便地迁移过去

tcdw's avatar

如何在已有的 Vue CLI 项目使用 esbuild


我们的门户网站项目(Vue 2 / Vue CLI 5 / TypeScript / Element UI)前端部分编译所需时间太长,因此开始考虑在不对已有项目进行过于伤筋动骨的调整的前提下,提升编译速度的方式。

由于 Vite 使用了 esbuild 进行编译速度的提升,我们想到了一个主意:借助 esbuild-loader,把 Vue CLI 中的 Babel 替换为 esbuild。


在项目根目录执行 npx vue-cli-service inspect,可以看到最终生成的 Webpack 配置中,以下部分涉及到了 babel-loader:

      /* config.module.rule('js') */
        test: /\.m?jsx?$/,
        exclude: [
          function () { /* omitted long function */ }
        use: [
          /* config.module.rule('js').use('babel-loader') */
          // (略)
      /* config.module.rule('ts') */
        test: /\.ts$/,
        use: [
          /* config.module.rule('ts').use('babel-loader') */
          // (略)
          /* config.module.rule('ts').use('ts-loader') */
          // (略)
      /* config.module.rule('tsx') */
        test: /\.tsx$/,
        use: [
          /* config.module.rule('tsx').use('babel-loader') */
          // (略)
          /* config.module.rule('tsx').use('ts-loader') */
          // (略)

由此可知,我们需要将 jststsx 的默认规则进行清空处理,然后让 jststsx 使用 esbuild-loader


首先,安装 esbuild-loader

npm i -D esbuild-loader

然后,在 vue.config.js 下的 chainWebpack 中加入以下内容:

// 清空已有的使用 `babel-loader` 的规则

// 注入使用 `esbuild-loader` 的新规则
        loader: "jsx",
        target: "es2015"
        loader: "ts",
        target: "es2015"
        loader: "tsx",
        target: "es2015"



  • 如果项目中有使用到 Web Worker,肯定会炸掉,出现 TypeError: Failed to construct 'URL': Invalid URL 错误。采用 Babel 则正常工作。


尽管 esbuild 是好文明,我们依然无法在那个门户网站项目中使用;因为我们项目要求兼容 IE11,而 esbuild 目前最低只能编译到 ES2015,这就导致编译产物无法在 IE11 等不(完全)支持 ES2015 的浏览器中运行;不过,在本地调试时还是很爽的,因为开发环境冷启动和热更新速度确实快了不少。

不过,如果你的项目不需要兼容 IE11 等老浏览器,但恰好还没有升级到 Vue 3 + Vite,完全可以试一试。能快一点是一点啊)

's avatar

Memory Order


Memory order是什么?哎呀怎么这个都不懂。Memory order不是很简单嘛!Memory是记忆,order是顺序。理所当然,memory order是记忆的顺序!当然,我记性不是很好,经常记不清顺序——卡特列夫库——是什么的库?






依云's avatar

从 getmail6 到 offlineimap

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



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


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/

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

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

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

从 getmail6 到 offlineimap

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

accounts = gmail
maxsyncaccounts = 10
socktimeout = 60
pythonfile = ~/.offlineimap/

[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 =

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

oauth2_client_id_eval = get_client_id("")
oauth2_client_secret_eval = get_client_secret("")
oauth2_access_token_eval = get_access_token("")

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


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:
  return _LOADED_DATA[account]['client_id']

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

def get_access_token(account):
  cmd = [
  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]/草稿',

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

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

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

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



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

  ~/.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:回复:」这类糟糕的邮件标题和过滤掉自动回复了。

依云's avatar


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


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


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

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


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




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






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

依云's avatar

Qt 的字体渲染问题

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

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


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

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

  <!-- Adjust font order for Qt applications -->
      <!-- 格拉哥里字母:Ⰽⱁⱀⱄⱅⰰⱀⱅⰹⱀ Ⰹⱍⰹⰳⱁⰲ -->
      <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>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>

这个配置的意思是,把这些字体的优先级提高一些。当使用 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 的评论。)

's avatar

记一次自建 Gitea + Drone 实例被挖矿的经历

「关于我自建 Gitea + Drone 实例然后被人一瞬挖矿这件事」
依云's avatar

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

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

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



正常 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 协议不支持,好歹还有绕过的办法。

RecursiveG's avatar

Rclone 同步 OneDrive for Business 共享

使用 OneDrive for Business 的 WebDAV 接口访问,无需登录。先去浏览器访问你拿到的分享链接,地址栏应该会变成如下形式:


然后打开 F12 找到一个叫 FedAuth 的 Cookie:


然后用命令行在 rclone 里添加一个 WebDAV 的远程地址。语法是这样的:

rclone config create <name> <type> <key>=<val> <key>=<val> ...


rclone config create <随便> webdav 'url=https://[YOUR-DOMAIN][YOUR-EMAIL]/Documents' 'headers=Cookie,FedAuth=77u/...'

细节请根据浏览器里的信息自己调节,如果设置无误就可以在 Rclone 里查看文件了:

rclone lsd '<你之前填的>:'


  • 暂不清楚这个 Cookie 的有效期是多长,如果 Cookie 失效的话自然就不能访问了。
  • 限速是存在的,如果 sync 的时候进度不动并且网络没流量通过,那大概是被限了。rclone 自己的速度计在速度为 0 的时候不会立即归零,而是 5MB,4MB,3MB… 这样慢慢下去,非常反直觉。限速时间还挺长的,暂时没有发现好办法能绕过。
  • 暂不清楚是不是所有的分享链接都可以用这种方式访问,可能需要组织管理员开启 WebDAV 功能?
ホロ's avatar

咱和()的 2021 年

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

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

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


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


tcdw's avatar

失败经历:为什么 Pomment 至今没有正式发布

从 2015 年开始,我的博客是一直在使用外挂评论系统的,从多说到 Disqus。其实这些评论系统本身没什么问题,但是中间发生了一些事情:

  • 2016 年下旬,Disqus 无法在大陆地区访问
  • 2017 年上旬,多说宣布关闭

博客圈里有很多像我这样的难民,他们需要一套替代的评论系统解决方案,但当时市面上缺少足够好用的自建评论系统(并不是没有,isso 其实就是一个)。因为我从小就没有玩伴,也从未有过很多朋友,我希望借助这个风潮来提升自己在圈子的知名度。于是,在 2017 年上旬,Pomment 开工了。


最早版本的 Pomment 就是采用 Node.js 编写的(其实后来几个版本都是)。由于我当时是高三党,加上开发经验严重欠缺,以前只是使用 JavaScript 写过一些小玩具,因此程序设计非常糟糕,以至于在后期难以继续往下编写,最终在 5 月弃坑。


2018 年,我系统的学习了 JavaScript 的基础知识和 ECMAScript 2015 的新功能,雄心勃勃的决定再次尝试开发 Pomment。

这一次,我希望为 Pomment 增加足够多的功能,来吸引很多博主使用 Pomment。但是,这些功能都是我自己根本用不到的:

  • 基于用户 IP 的留言自编辑/删除功能
  • 插件系统,可以开发反垃圾邮件以及提醒邮件发送的 Provider
  • 功能齐全的 Webhook 和 Telegram Bot
  • 遵循 GDPR 的访客自助服务功能……

但实际上,由于这些功能我自己根本不会使用,加上功能数量过多,导致软件的开发和测试成本直线上升,拖累了整个项目的进展;同时,由于 JavaScript 本身属于弱类型语言,加上自身编码水平仍然较为欠缺,导致程序运行开销巨大、存在大量隐性错误,难以继续维护;尽管程序早在 2018 年 5 月就在自己博客上线,但是当时线上版本的问题较为严重,同时产生了大量的脏数据。

v2 和 v3

为了解决 v1 的问题,在 2018 年 9 月,我决定对整个程序进行重构。这一次的亮点是,我首次采用了无数据库设计(所有数据存储在若干 JSON 文件中);相比通过 Node.js 的 ORM 调用数据库,效率反而更高,而且更加节省内存。

然而,我依然迷恋于堆积自己不需要的功能(尽管在这一版本中删除了一些),导致系统质量和开发效率依然欠佳。同时,博客圈的风向开始发生转变:越来越多的人青睐无后端、静态的博客及评论解决方案,同时一些新兴的评论系统(例如 gitment)巧妙地借助 GitHub 等公共服务存储评论数据,使得 Pomment 的地位也变得非常尴尬。毕竟博客本体都已经是静态/无后端的了,谁还希望专门为评论系统租用 PaaS 服务呢?

v3 则是在 v2 的基础上迁移到了 TypeScript,并修正了不少的类型相关编码错误;同时,还使用 Vue 2 编写了一套管理界面。目前的 Pomment 版本其实已经较为成熟,但是在现代的博客生态之下受到青睐已经是不太可能了。

遗产和未来的 v4

随着我大学毕业以及需要寻找工作,如何让自己的简历更加出彩成为了我需要考虑的事情。而正是由于我开发了 Pomment,我可以大方的把它写在我的简历上,同时对于整个项目的开发过程、遇到的坑洞都有很多可以说的地方。事实上,我不觉得我的评论系统程序本身是失败的,但过程是。


在明确了我开发开源软件的目的是为自己服务以后,Pomment v4 的愿景就清晰起来了:使用 Golang 重写,只注重自己需要的功能,把更多的精力放在打磨特定的功能而非堆积功能数量(质量重于数量)。

's avatar


已经两整年没有更新过这里了。如果只是看我在这里发的文本而没有通过其他途径了解我的话,大概会还在担心我现在的状态吧。那么在这新的一年到来之际,来汇报一下 BPC 的状况。

滚出 THU

总而言之,我成功在 2020-2021 学年补上了毕业设计,成功地把结业证书换成了毕业证书。


于是在 2020 年的 5 月,因为感觉调整过来了一些,需要找点事情做,所以我去给我正在读研的的同届同学做的 zCore 修 bug 去了。它是一个用 Rust 编写,运用了 Rust 的 async 语言机制,参考谷歌的 Fuchsia 里的 Zircon 斯县的一个操作系统内核。借此机会我被这位同学把介绍给了他的导师,于是之后的一整年,我跟着他的导师做了一些操作系统方面的事情,并且完成了毕业设计。虽然中间是出了一点事故和波折,但是最终还是完成了,可喜可贺。

作为毕业设计的内容,我重新写了半个操作系统内核,起名叫 breakcore。虽然没能完全写完,但是基本的动态内存管理和上下文切换都是有的。作为方便调试的设施和毕业设计中的亮点,我还想办法让它在 panic 的时候打印的调用栈信息里附带上了从内核二进制的 debuginfo 里读出来的,精确到文件、函数、行号列号的调试信息。虽然之后可能不太有机会完成剩余部分了,但是各位如果有兴趣的话,也可以看一看。


毕业之后,我休息了几个月。在那之后,我在 2021 年的 8 月获得了一份全职的开发工作。这份工作是由 Telegram 上的一位网友介绍的,他看到本站的背景动画之后,觉得我的图形学水准大概能在他所在的公司发挥很大的作用。最后当然是顺利地通过面试,迅速入职了。

不过由于各种原因,我暂时不能透露公司的名字和具体业务。我的工作内容是交互内容的开发,从图形到音频,从具体实现到底层架构都有一些涉及,技术栈上的话则是以 Web 平台为主。



2020 年的 4 月,我如约给 Pikanote 建立了 专门的站点 ,最然更新频率依旧不高,但是确实是写了一些新东西。最近的文章是 USTC Hackergame 2021 Writeup,是我参加中国科学技术大学第八届信息安全大赛后写的题解,当时排名还挺高的,也是让我重新认识了一下自己能有多强。

CanvasArt 也进行了更新,加入了近年来本站的背景动画效果。和在这里使用的版本相比,那边的版本加入了很多可以调整的参数,大家可以玩玩看。

另外就是,工作之后有了收入,我终于可以攒新机器了,新机器搭载了 RTX 3070 Ti 的显卡,Ryzen 9 5900X 的处理器,性能上和之前 2015 年买的笔记本相比有了质的飞跃。由于新设备不再会有视频编码性能上的问题,我终于可以重新开始直播了,休息日的午后也许能在 BPC 的直播间 看 BPC 打游戏。虽然网络有的时候不太稳定,音频输入的声音也比较小,但是之后大概会有机会加以改善吧。



BPC 的生日也不远了。从 v14 开始自己画头像和背景设计到现在,已经算是满十年了。今年当然也会换上新的 v24 的头像和背景设计。虽然还没有实际开始实现,但是我心中已经有想法了,敬请期待。

今年大概还会做一些新的技术上的尝试,补全一下我在本科期间没能学到的一些东西,解决一些怨念。如果时间宽裕的话,也许说不定还会有空重启 F70 写一点自己的游戏?

另外在非技术的部分也希望有些进步,既然有了高性能的显卡,当然不能浪费他的算力。总之希望今年能有机会研究一下 Blender 和其他的一些东西,尝试做一些 Motion Design 的内容。除此之外,说不定也许能搞出一些别的玩意儿?


tcdw's avatar

我的 2021

于是,2021 年猝不及防的结束了。虽然我很多时候懒得写这种总结,但是考虑到我还有一个博客在运行,还是写一篇总结好了。



同时,今年我把我的 Homelab 建设了起来。我通过购买一些全新/二手设备和配件,完成了我业余机房的基础建设。

功能上,可以分为图形虚拟化服务器、Linux 虚拟化服务器、云游戏服务器、NAS 和软路由;同时,还借助一台阿里云 ECS 实现端口转发、VPN 等功能。当然,这样的业余机房也不是没有代价的;一个是噪音比较大(就这我还没考虑机架式服务器),还有一个是电费要比以前高出不少(好在内蒙古的电价相对还是很低的)。但是,这套 Homelab 极大的改善了我(自认为是 Power User)的 IT 生活体验,包括云游戏也可以让我在任何地方享受家里安装的 PC 游戏。

说到 PC 游戏,其实这一块一直是我最缺乏了解的领域。我主要的游戏经历都是在家里的山寨 FC、后来(打着 MP4 旗号)的模拟器机、智能手机,以及 Nintendo Switch 上;由于以前家里的 PC 配置并不理想,加上父母的态度对 PC 游戏并不友好,我在 PC 上玩过的游戏寥寥无几(不过包括 Minecraft)。其实我开始涉足云游戏,只是希望拥有更好的远程桌面体验;虽然今年显卡价格蹭蹭蹭的上涨,但是因为我去年恰好买了张索泰的 GTX 1650 Super 当亮机卡(迷惑行为),我误打误撞的就开始玩起了《原神》,结果我一发不可收拾,还注册了 Steam 账号,开始研究我还能在 PC 上玩些什么有趣的游戏。因为就算是 1650 Super 这种入门级游戏显卡,它带来的游戏体验也是碾压对我来说的传统游戏设备(至少现今),而且键鼠操作也让我感觉更加灵活、舒适。

不过我也发现很多 PC 游戏都有着烦人的 DRM 和反作弊措施 (aka rootkit)。虽然我不认为我是什么隐私怪,但这让我感觉并不太爽;同时因为我工作站的十代 i7 算不上玩游戏的最佳选择,我从群友那边收了一套 B450 + Ryzen 5 3600 的套装,用手上的一些东西组装了专用的云游戏服务器。结果我的 1650 Super 反而成瓶颈了(


随着我不断接近毕业,学校的事情也变得越来越少。于是今年年初,在 @qwe7002 的介绍下,我开始利用空闲时间做一些前端开发的远程兼职,并陆续参与了几个项目:

  • 某微信小程序(2 月至 4 月)。这个小程序是经过了几个外包开发者之手,代码逻辑混乱不堪。我经过不懈努力,成功的重构了一小部分浑浊不清的代码,还写了几个新的页面。不过这个项目的经历也让我明白 TypeScript 对于这种多人合作的大型 JS 项目真的很关键,同时对小程序的好感进一步受损,因为这玩意即使是对开发者也很不友好啊。
  • 某 ERP 系统前端(3 月至年底)。这一次,我和另外几位小伙伴,从零开始了一个比较庞大的 Vue 2 项目。我参与了整个项目的选型和脚手架搭建工作,同时意志坚定的选择了 TypeScript(虽然代码里还是有一大坨 any。这个项目还是让我感觉非常愉悦的,因为毫无大型商业项目开发经验的我们居然成功的把项目做了出来,积累了不少经验;而且,还成功让我的小伙伴们掌握了 TypeScript 的基本使用(他们以前都只有 JavaScript 的使用经验);而最重要的是,我拿到了一笔非常可观的报酬。

12 月,我顺利的完成了毕业设计和毕设答辩,进入实习阶段。2022 年起,我将在福州开始我人生中的全新篇章——成为社畜。虽然不知道以后还会发生什么,但是我对我的未来还是充满期待的。



以及,我开始用 Golang 重构我博客的评论系统了。原来写的 Node.js 后端虽然能用,但是算上依赖以后的体积非常臃肿,而且非常吃内存(要知道我的 VPS 也就是 1c1g 的配置);同时,我想找个东西来练练 Golang,所以就拿自己博客的评论系统开刀了。

目前还没有写完,但是到时候大概可以给我可怜的 VPS 节约不少内存吧。


2022 年对于我来说会是变化最大的一年,因为我的身份要从学生变成社畜了,同时以后要开始在南方城市常年驻扎。所以未来的事情,只能说走一步看一步吧。

听某些人说,2022 年意味着 2020 年的重复,因为 2022 年拿英文读听起来像 Two thousand twenty too(也是 2020 年)。2021 年的世界依然不太平,所以只能希望 2022 年不要真的是这样,以及第三次世界大战永远不要发生了(至少在我的整个人生中)。


SgDylan's avatar



tcdw's avatar



比如玩个 Minecraft SMP 服务器,玩到后期就开始各种内卷了,比如哪个聚落有更好的生产设施、更美丽宏伟的建筑、更多的物产等。



结果,我发现我真正需要的是像《原神》这样奇怪的网游。它没有 PvP、没有排行榜,可选的好友联机也仅限于合作 PvE 和收集资源。



SgDylan's avatar

RouterOS 接入 IPv6 记录

记录 RouterOS 配置 IPv6 的过程。

依云's avatar

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 程序弹出菜单会显示在错误的位置。


Phoenix Nemo's avatar

配置 Fail2ban 保护 Proxmox VE

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

创建 filter

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

failregex = pvedaemon\[.*authentication failure; rhost=<HOST> user=.* msg=.*
ignoreregex =

创建 jail

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

enabled = true
port = https,http,8006
filter = proxmox
logpath = /var/log/daemon.log
maxretry = 3
# 1 hour
bantime = 3600

重启 fail2ban

~> systemctl restart fail2ban


~> fail2ban-client status
|- Number of jail: 2
`- Jail list: proxmox, sshd
's avatar


在 RSS 中无法显示完整的格式,不同的 RSS 阅读器所显示的内容也可能会有很大差距。为了能够获取到完整的内容,最好还是点开网页链接看。



黄鼠高中的成绩很差,因此没有足够的分数在大学进入自己心仪的计算机科学专业,而是不得不学了一个算是相关的专业,网络工程。 4 年即将过去,黄鼠感觉在学校中似乎并没有学到很多(值得花费 4 年的)东西,时间似乎都花在了来回的交通,关心各种行政事物,还有满足各种繁杂考核标准上。倒是在自己的时间里进行的学习,似乎反而还不如中学时的多。有趣的课程是存在的,可是再有趣的东西,被掩埋在时间压力、行政事物、标准考核下,再被批判的目光盯着,并且这个批判目光可以非常轻易地使黄鼠白交学费、白花时间、延迟毕业、打乱一切计划——的时候,也变得枯燥乏味了。于是黄鼠采用了这样的心态:不指望从学校学到什么,主要的能力靠自学,从学校只是为了得到文凭。但是大概是因为自己比高中时变废了,并且也因为在课业中已经敲电脑足够累了,黄鼠在课外进行的自我提升反而减少了。


现在黄鼠就要从网络工程本科毕业了,终于也许可以有机会在硕士去搞计算机科学了。这两个应该向是相关的专业,想要过去应该是蛮直接的吧?可是光是最低要求就使黄鼠与许多学校的计算机科学硕士无缘:有些是说必须要有计算机科学的学士学位,有些是在后面加注了“或同等学位”。黄鼠怎么知道自己的“网络工程”是不是算是同等?有些地方说这个要求是故意写的这么模糊的,要看每个人的具体情况,这个黄鼠喜欢;但有些地方写了很明确的怎样算是“与计算机科学学士同等”,要包含某些特定的课程,像是“基础算法”、“离散数学”、“数据结构”、“面向对象编程”什么的——都是黄鼠没有在学校学过的东西。就在这时,黄鼠才意识到了自己的本科与真正的计算机科学本科是有多大的差距。编程黄鼠当然是会,但是并没有任何官方机构可以为黄鼠证明;而其它 3 个黄鼠是完全没有系统地学习过。即使是在没有列出这些明确要求的地方,黄鼠想他们大概也是会默认申请者都有这些知识的吧?也许是,也许不是,黄鼠不知道这会不会为自己在审阅者眼中减分。

黄鼠觉得自己还是有资格被称作“极客”的。但是仔细想一想,自己真的有做出什么出众的事情吗?听说贡献开源项目是一件极客们经常做的事情,可黄鼠没有为任何大型开源软件贡献过代码。看一眼自己的 GitHub ,黄鼠有贡献过的最大的开源项目有两个:一个是给软件的文档加了一句话,另一个是 Dress[1] 。听说在技术社区中活跃也是一件极客们经常做的事情,那黄鼠在 ArchLinux 群水群算吗?黄鼠没有打过包、贡献过翻译、管理过什么东西。黄鼠唯独能称作是极客的地方,无非就是写了一些个人项目,一些没什么挑战性、没有什么创新、几乎都是只有自己用、都是玩具质量的软件,换句话说,都是在“造轮子”。朋友建议说,即使是个人项目也可以写它创造了什么,解决了什么挑战。可是黄鼠全部在做的,都只是在学习怎样调用别人事先已经写好的框架,做出玩具的样子。造轮子能够有什么挑战和创新呢?黄鼠能够把自己的轮子们列举出来,并且说明一个个都是做什么的,但是那样听起来无比寒酸。放在企业里面,黄鼠能够做到的事情大概与 3 个月速成培训班出来的人无异,是像民工那样堆砖砌瓦的工作。堆砖砌瓦当然很重要,但是黄鼠不想要做那样的事情一辈子,要是想的话,完全不需要去读硕士。

黄鼠造过什么轮子?有许多 Telegram bot (聊天机器人),大量的 bot 。有会在群里随机发表情的 bot ,有生成图片的 bot ……尽管写它们的过程对于黄鼠来说都很有趣,并且有网友喜欢,但显然不能够被称作是任何“技术壮举”。黄鼠做过一个 LED 时钟 ,但是那只是包含了一些基础又粗糙的木工,一些用几个小时就可以上手的电路板绘制,以及一些面向初学者的嵌入式软件开发。黄鼠在做完时钟之后,被网友推荐加入了一个“嵌入式”的群,然而群友们在说的话,大部分时候黄鼠都听不懂。他们都比大概与黄鼠年龄相仿,讨论的内容却高几个层次。黄鼠想,自己要申请研究生的话,竞争对手们大概有许多都是那样的人。








然后黄鼠成功地被拖延症带领着写完了这篇博文,自荐信正文却还是只有几行 _(:3)<)_ 。

“直接把这篇博文当作自荐信发过去吧。”黄鼠有这样的冲动,但是那样大概就是自暴自弃了。 🐁

  1. 一位朋友说,如果自己是跨性别者什么的,也可以写进去。可是……抛开黄鼠是不是不谈,即使是,写进去也不能够是自己变得更被审阅者看好吧?总不见得大学招生会有 LGBT 优待什么的。倒不如说可能会使人觉得申请人在强行博取同情。

除特殊说明以外,本网站原创内容采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。转载时请注明来源 FiveYellowMice 的博客 ,以及原文链接

Horo's avatar

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



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

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

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

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

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

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


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

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

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

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

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

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

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

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

于是接下来就是延续到现在的折腾 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)

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

tcdw's avatar

npm、镜像源与 package-lock.json

在国内开发涉及 Node.js 的应用都知道,裸连官方的 非常慢,等待时间令人捉急。

解决这种问题,我们自然想到的就是找镜像源(就像 Linux 发行版的包管理器那样)。

国内目前已经有一些 npm 镜像源,大多数情况下,它们其实还是可以用的。但是,package-lock.json 中会记录下各个包的原始 URL:

    "node_modules/@babel/compat-data": {
        "version": "7.16.0",
        "resolved": "",
        "integrity": "sha512-DGjt2QZse5SGd9nfOSqO4WLJ8NN/oHkijbXbPrxuoJO3oIPJL3TciZs9FX+cOHNiY9E9l0opL8g7BmLe3T+9ew==",
        "dev": true,
        "license": "MIT",
        "engines": {
            "node": ">=6.9.0"
    "node_modules/@babel/core": {
        "version": "7.16.0",
        "resolved": "",
        "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==",
        "dev": true,
        "license": "MIT"

其实如果项目仅仅在国内开发和编译,问题不会很大。但是如果你开发的是开源项目(特别是面向海外的)或者在国外服务器跑 CI,你的项目就有可能会遇到安装依赖时连接不稳定的问题(因为国内这些镜像源通常不会针对国外优化)。


  • 尽量避免使用镜像源。
    • 进行肉翻。
    • 对于连接缓慢的问题,可以给 npm 设置代理,或者利用国外 VPS 为 npm 自建 SNI Proxy,然后在本地修改 hosts 文件/进行 DNS 劫持。
  • 如果一定要使用镜像源,则需要:
    • 考虑清楚你的项目是否将完全在国内开发,并让团队中所有人使用相同的镜像源。
    • 尽量选择能够通过 sed 简单替换 package-lock.json 中所记载地址的镜像源(例如华为源、中科大源)。
Phoenix Nemo's avatar

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

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

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


set mirror http://archlinux.mirror/archlinux/iso/latest
set script http://unattended.install.script/
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

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

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

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



ホロ's avatar

带键盘的 Android 手机和多亲 F21 Pro 折腾记

因为人(?)老了就喜欢怀旧? 😂

对咱这个高不成低不就不知道算不算老人的家伙来说呢,能够拿来怀旧的应该会有那些“古老”的 手机游戏了。(也没有多么老啦,就是差不多十年前那些 Java 手机游戏了,好像叫啥 J2ME? 算了不管了后面就都叫 Java 了,希望大家不会和现在还在的那个 Java 搞混。)

虽然现在也有像是 J2ME-Loader 这种 Android 上也可以用的 Java 模拟器了,但是在触摸屏上按虚拟按键完全没有感觉嘛…… 所以咱也开始物色有键盘的 Android 手机了。

好啦咱知道去收购一些老的手机也是可行的解决方案,那下面的内容就完全没有必要写了。 是不是咱可以鸽了 (x)

举几个键盘 Android 手机的栗子

虽然 Android 的操作更多的是以触屏为主,但是也不是没有有物理键盘的机型啦。 比如摩托罗拉里程碑之类的,但是那个好像太古老了点……

最近几年最能拿出来举的例子大概就是黑莓了,例如最后一部亲自操刀的 Priv 和后面授权 TCL 设计生产的 KeyOne / Key2 。不过 TCL 也在 2020 年撒手不干了就是...

除了黑莓以外,也有几家公司设计过带物理键盘的 Android 手机,例如 Unhertz TitanF(x)tec

不过上面这些还是有一个微妙的问题,它们都是传统的 QWERTY 键盘布局,所以按键普遍的都比较小。 方向键也都在一些奇妙的地方或者干脆就没了。虽然大部分 Java 手机游戏都能用数字键代替方向键来着, 等等,这些手机上的数字键基本也要靠 Fn 之类的组合键触发出来啊……


后来上网瞎逛的时候就看到了标题说的那个 多亲 F21 Pro 。 是常见的 12 键键盘布局,Helio A20 的性能虽然不强,但是满足咱这需求应该是够用的, 于是就买了一台 4G+64G 版本。

在咱开始写这篇文章的时候,才发现还有一个 F21 Pro+,平台换成了紫光展锐 T310。 同时可以选择学生版和标准版(标准版可以自由安装应用,学生版不行,F21 Pro 也不行), 不过只有 3G+32G 版本可选,如果汝不想像后面那样如此麻烦的话,去买那个就行。 3+32 应该也足够用了,除非汝往里面装了几万个游戏那样的(笑)。


既然这都拿“学生手机”作为卖点了嘛,那自然不能自己安装应用就是设计使然而不是 Bug 咯。 这个系统貌似是魔改了系统的软件包安装程序,如果包名不在白名单里就会提示 “禁止安装第三方应用,请去应用市场安装”。 多亲似乎也从 2 Pro 那时学来了不少的反制措施, 例如封掉了 adb 安装,提前占了设备管理员应用的位置之类的。

不过后来有人发现系统自带的应用市场其实是能安装其它的应用的,只要把正在下载中的安装包换成汝想要的软件就行了。 就算后来的系统更新封堵了一阵子,也又被发现只要把旧版的应用市场装上就能绕过了……

也有人写出了能半自动操作的工具了,例如 MlgmXyysd/k61v1injector 。 但是咱比较懒(上面那个依赖 PHP),所以就手动操作一把了。

  • 在 F21 Pro 上启动开发者选项里的 USB 调试。和普通的 Android 手机一样的方法咱就不用再啰嗦了吧?
  • 接上电脑,在手机上授权电脑,然后打开一个终端。
  • 清除手机上应用市场的数据。(这是为了接下来偷懒方便(x))
  • 打开手机的应用市场,下载一个软件。考虑到下载的时间因素,建议选一个稍微大一点的软件, 或者限制一下手机的网速也是可行的。

然后在终端上输入(汝应该知道要把哪里换成汝自己要安装的 APK 文件的路径,对吧?):

# $() 可以把括号内的命令的结果替换到上层的输入里。
# 例如这里的 adb shell ls /sdcard/Android/data/com.duoqin.promarket/files/Download/
# 会列出这个目录(应用市场的下载目录)里的文件列表。
# 如果汝刚刚是清除了应用数据再下载的话,这里就只有汝当前正在下载的应用了。
# 于是瞒天过海大法发动(x)

$ adb push /path/to/your/apk \
/sdcard/Android/data/com.duoqin.promarket/files/Download/$(adb shell ls /sdcard/Android/data/com.duoqin.promarket/files/Download/)

不过这种方法唯一的缺陷就是,应用更新以后汝还是要这么来一遍,所以还是要研究一下怎么把这破烂限制解掉。 奈何都喜欢藏着掖着……

效果大概就是这个样子。于是汝也可以把刚刚咱说的那个 Java 模拟器装上,再找来几个老游戏的 JAR。 就可以愉快的玩耍啦。

因为 F21 Pro 的屏幕分辨率是 480x640 ,正好是以前最常见的 240x320 的两倍。 所以在模拟器里设置好全屏显示,再隐藏掉虚拟键盘以后的感觉就和以前差不多了。 至于按键手感嘛……这个只能说因人而异了。


  • 如今很多 App 都不会给键盘做优化了啊,还好 F21 Pro 有触摸屏,用起来不会像之前流行 的 C558 那样麻烦。
  • 以及该卡的国产“小而美”还是卡,刚装完能吃掉接近 1G 储存空间可还行……
  • fastboot flashing unlock 对这个是可用的,但是这个手机没有音量键没法确认啊 😂

以及这里面手机的截图其实是截取的电脑用 scrcpy 连接到手机的画面,就这样吧。

imi415's avatar

MIPI-DSI LCD with Zynq

Driving a cheap DSI LCD with Zynq
Yeechan Lu's avatar

我的浏览器地址栏中的 A – Z(2021)

前略,天国的 2017 秋酱。好久不见!

  • Adminer.REDACTED (local Adminer instance)
  • Z3.REDACTED (local MinIO dashboard)
Horo's avatar

如何比较安全的配置 Web 服务器 - TLS 和 HTTPS


又是一次知道的已经做好了,不知道的对着他耳朵唠叨也不会去做的系列节目。(嗯?) 如果不想听咱啰 …

RecursiveG's avatar

LTO-5 磁带机折腾入门

最近在 eBay 上捡了一台 HP 的 LTO-5 磁带机,型号是 BRSLA-0903-DC,这次就把折腾过程简单记录一下。
LTO 磁带机本体一般都是标准的 5.25 寸光驱位大小,接口类型一般是 Fiber Channel 或者是 SAS 加上供电用的 Molex 4Pin。
不管哪种接口你都需要一张对应的 HBA 卡插在 PCI-E 槽里,以及对应的光纤线或者是 SAS 线把磁带机接到卡上。
如果机箱带光驱位,SAS 的磁带机可以直接放在机箱里,FC 的应该就不行了,因为我还没找到接口朝内部的 FC HBA 卡。



我买到的是一台 FC 接口的磁带机,注意这张图里的磁带机外面还有一个转接架,是给磁带库用的,自己用需要拆掉。

对应的光纤通道 HBA 卡




所有以上硬件再加上 5 盘磁带大概一共花了 300 美刀左右。顺便吐槽一下 LTO-6 磁带机的拍卖价格真疯狂(600+)。

TAR (Tape ARchive)

磁带机应该会在 Linux 下显示为/dev/st0/dev/nst0设备,区别在于st设备会在任何操作后将磁带倒带回开头,而nst设备会将磁带停留在操作结束的地方。


tar -cvf /dev/nst0 filename


tar -xvf /dev/nst0


tar -tf /dev/st0

更多操作可以参考这篇文档: How to archive data using the AIT2 attached to CDF17

LTFS (Linear Tape File System)

LTO 从 LTO-5 开始支持 LTFS,你可以先将磁带“格式化”,然后用更为熟悉的目录结构来管理磁带上的文件。你需要自己从 LTFS 的 GitHub 源码 编译。Arch 用户也可以直接用 AUR 包。具体使用方法在 Quick Start 里已经写得很清楚了:

1.列出设备(如果没有可以尝试手动modprobe sg

sudo ltfs -o device_list


sudo mkltfs -d /dev/sgX


sudo ltfs -o devname=/dev/sgX /ltfs


sudo umount /ltfs


磁带机买来干什么?鉴于磁带的顺序读写以及需要手动换磁带(买带库的大佬请忽略)的特性,注定了它只适合于备份和归档用途。备份像是重疾保险,你希望永远也用不上;归档是你 6 岁时的玩具,舍不得丢但也不会再用。所以如果你说 NAS 空间不够,要用磁带来存你的电影,我觉得不太行。但如果你仓鼠症发作,打算收集世界上所有的电影,磁带大概可行。

我还顺便搜集了一下 HP 的官方文档,方便参考:

RecursiveG's avatar

USB4 与 Thunderbolt 4 备忘

最近研究了一些关于 USB4 以及 Thunderbolt 4 的资料,在此做个备忘。目前只考虑 USB Type-C 接口,并且忽略 Type-C 可以正反随意插带来的复杂性。

  1. USB Type-C 接口里有一对(两根)差分信号线,用于 USB 2.0 协议(USB 2.0, 480 Mbps)。
  2. USB Type-C 接口里有四根 GND 以及四根 V_BUS 用于送电,具体电压和电流由两端使用 CC 线协商 (Power delivery)。
  3. 在 Type-C 接口里再取两对差分信号线,用于 USB 3.x 协议(USB 3.2 Gen 1, 5 Gbps)。
  4. 通过改进协议,可以将传输速度翻倍(USB 3.2 Gen 2, 10 Gbps)。
  5. 再把 Type-C 中最后剩下四根信号线也用上,传输一样的协议,可以将速度再次翻倍(USB 3.2 Gen 2x2, 20 Gbps)。
  6. 大家发现这八根高速信号线不止可以用于 USB,也可以用来传输其他信号。比如:电脑可以与显示器透过 CC 线协商,使用两对差分信号线传输 DisplayPort 信号(DP Alt mode)。
  7. Intel 觉得 Type-C 接口不错,于是有了 Thunderbolt Alt mode,使用全部四对差分信号线传输 Thunderbolt 协议(Thunderbolt 3, 40Gbps)。
    Thunderbolt 3 本身是一种隧道协议,在这个隧道中可以传输 PCI-E 数据和 DisplayPort 数据。
    至于 USB 则可以在扩展坞中内置一个 USB 控制器芯片,通过 PCI-E 与电脑连接,这样扩展坞就可以插 USB 设备了。
  8. USB-IF 觉得 Thunderbolt 3 这个协议不错,在 Intel 开放了 Thunderbolt 3 协议后,就把它拿过来“改名”成了 USB4。使用 USB Type-C 中的两对或四对差分信号线传输(USB4, ~40Gbps)。
    与 Thunderbolt 3 相同,USB4 也是一种隧道协议,其中可以传输 USB 3.2,PCI-E,DisplayPort 等协议。
    (吐槽时间:外层协议和内层协议都叫 USB 你是认真的吗?)
    USB4 规范并不要求硬件生产厂家实现所有功能,比如说,一个最高只支持 20Gbps 速度的设备可以合法地被称作“支持 USB4”。因为 USB4 规范并不要求所有设备都支持 40Gbps。(吔屎啦你 USB-IF)
  9. Intel 觉得 USB-IF 的标准混乱,是赚钱的好机会,于是自己列了一套更高的标准(比如要求设备必须支持 40Gbps 速度),并给符合 Intel 的标准的设备贴上 “Thunderbolt 4” 的标签。

最后围观 USB4 混乱的速度要求被鞭尸的现场:

Allison Sheridan: So a Thunderbolt 4 device is a USB4 device...Brad Saunders:    (nodding)Allison Sheridan: ...but a USB4 device is not necessarily a Thunderbolt 4 device?Brad Saunders:    It can be ...Allison Sheridan: It can be but it isn't necessarily.Brad Saunders:    It'll probably have... they may have made a choice to... maybe it's only 20 Gbps.Allison Sheridan: Right, but it's Thunderbolt 4 it's 40 [Gbps] per second, Okay.
Phoenix Nemo's avatar

修复 LVM XFS 的 Input/output error


设备被强制重启之后发现 LVM 满了,但是文件无法访问,所有文件操作显示 Input/output error

HY's avatar


现在越来越多的游戏开始加入了开箱抽卡系统,用作游戏的主要营收方式。 这里我们先不提及开箱抽卡作为赌博的本质,单 […]
's avatar

More Than a Yuri Story: Thoughts on Heart of the Woods

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.

Characters in the title screen: Abigail (left), Maddie (right)

Heart of the Woods looks like a nice interesting love story about 2 girls, one human, one ghost. It’s homo, so it’s just what I needed to escape from the real world. Although it appears to involve love across the living and the dead, which is a concept that’s been done to literal death. But hey, the game is well-received, it’s on sale, the artstyle is new to me, and most importantly, it doesn’t appear to be too long. So I guessed it’s going to be a nice fit for the spare time I currently have.

Boy was I wrong. It does not use the old plot device of "one grows old and die while the other stay for eternity" or "the border between life and death is unbreakable so we can’t be together" that human-ghost relationship stories tend to have, which I imagined to also be a leading aspect of this one, too. Heart of the Woods has so much more than that.


There is no spoiler beyond the first chapter.

The opening scene, Tara (left) and Maddie (right) lie on the train seats

The premise of the story involves a YouTuber that talks about paranormal events, Tara, and the assistant, Maddie. The two travels to a supposedly-haunted town to investigate for their show, because a local of the town as well as a viewer of the show, Morgan, invited them. The ghost appears much later in the story, and is named Abigail (feels pretty unimaginative for a ghost name, to be honest).

At this point, even though none of the interesting things have surfaced. I was quickly drawn in by the sheer beautiful writing. Maddie’s opening monologue very effectively introduced a realistic situation: Maddie has been a long-time friend to Tara, and an assistant to her paranormal events show, but Maddie was going to quit and chase different life goals after this trip to the haunted town. Her guilt, her internal struggles and worry for Tara and all the complex emotions was very expressively shown. I felt I could be in Maddie’s shoes. Through her interactions with Tara, I could feel the awkwardness between them. Tara is salty, Maddie is guilty, but they also know they should keep their cool, but at the same time it’s also really hard to get over their emotions, however they still have a final episode to make…​ This kept going for a long time, it felt real.

It felt funny that I came to this game because of the official pair of Maddie and Abigail, but before that main course came, I was already fascinated by the relationship of Maddie and Tara. I was already worried about them. As Morgan teased them with paranormal phenomena, Tara was hyped and ready to believe, while Maddie was skeptical. Maddie thought Morgan was maniac for claiming crazy things but can’t prove them, and for her infame among townspeople. I actually shared Maddie’s opinion for a while, even though as a reader, I know for a fact that there’s a ghost and so Morgan is at least partially right.

Throughout the former half of the story, the situation keeps getting more complex, and the stakes keeps getting higher. Even without half of the official pair (Abigail) appearing, the 3 characters already seem so interesting. I was just expecting a sweet yuri story, but as I read it, it becomes full-on suspenseful. The suspense peaked at the end of the first chapter, but it did not just decline after that. Things keeps changing, and there is little to no filler texts, no boring segments about "daily lives".

I really liked the romantic parts of the story as well, they complement with the suspenseful parts perfectly. With such good writing, the characters are fleshed out, and their thoughts feels grounded. Their relationships feels human. It’s lovely. With no spoilers, allow me to say that there are lots of hugs, cuddles, and kisses. I felt them, embarrassingly. Even though I’m usually not a fan of erotic scenes in visual novels, I would probably sit through them if this game had any. (There is actually an R18 patch, I didn’t know about it.)

An interesting thing I liked about the character settings is about Tara. The first thing I noticed about her is the voice acting. Her voice sounds unexpectedly deep, almost like…​ the voice a transgender person could make. I thought "it can’t be", but I looked at the credits, and apparently Studio Élan did find a transgender voice actor, and Tara did mention in one line of dialogue that she is trans. The impressive thing is that nobody in the game ever cared about Tara’s voice, even ones that are unfriendly. Tara being trans is not part of the plot and only briefly mentioned. I think this is a massive "trans rights!" shout of how they are just regular people being themselves and we don’t make dramas on their gender. I’m behaving worse than the characters in the game because I’m making a drama about it in this paragraph.

A guest art by Roarke taken from the game’s guest art gallery. I died as I see this.

Let me hope that this article has made you give some interest to the visual novel. It is sold on and Steam. Ultimately, I think my experience with Heart of the Woods proves the point in my previous article, how good a story feels comes from subverting expectations. This novel has subverted mine a lot, and hopefully I have not spoiled it too much to let it also subvert yours. 🐁

Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

RecursiveG's avatar

手动硬盘安装 WePE

最近尝试了一些 Windows 下的全盘备份/恢复方案,于是顺便折腾了一下各种 WinPE 系统。WinPE 简单来说就是一个 Windows 的 LiveCD,带有各种用于 Windows 的工具。网友们在微软的 WinPE 基础上加入各种驱动和方便使用的图形化操作界面,作为不同的 WinPE “发行版”发布,微PE(WePE) 是这些“发行版”之一。

WePE 自带的安装程序除了安装必要的启动项以外还会安装一些没啥用的选项。所以记录一下手动安装启动项的方法。环境为 Windows 10 64位 UEFI 启动。你需要先制作 WePE 的 ISO,然后把 ISO 里的WEPE目录复制到随便一个分区里,我假设是D:。用管理员身份执行以下命令:

bcdedit /create /device
# 你会拿到一串 GUID, 假设是 {A...}
bcdedit /set "{A...}" ramdisksdidevice partition=D:
bcdedit /set "{A...}" ramdisksdipath \WEPE\WEPE.SDI

bcdedit /create /d "WePE" /application osloader
# 你会拿到一串 GUID, 假设是 {B...}
bcdedit /set "{B...}" device "ramdisk=[D:]\WEPE\WEPE64.WIM,{A...}"
bcdedit /set "{B...}" osdevice "ramdisk=[D:]\WEPE\WEPE64.WIM,{A...}"
bcdedit /set "{B...}" path \windows\system32\boot\winload.efi
bcdedit /set "{B...}" systemroot \windows
bcdedit /set "{B...}" nx OptIn
bcdedit /set "{B...}" pae ForceEnable
bcdedit /set "{B...}" detecthal Yes
bcdedit /set "{B...}" winpe yes
bcdedit /displayorder "{B...}" /addlast
bcdedit /timeout 3


SgDylan's avatar

Imitate DSEE HX

两年前的坑 Remake,大概是最接近 DSEE HX 的实现。

tcdw's avatar




  • 只有一台能带动这游戏的远程工作站,我还很难物理访问它
  • 平时在外面只能使用弱鸡的旧款 MacBook Pro
  • 用 iPhone 12 Pro 玩,感觉体验堪忧


考虑到近年来 StadiaGeForce Now 等云游戏服务在国外的兴起,我有了新的主意:拿自己的远程工作站搭建云游戏服务。



  • 服务端(工作站)和客户端都在重重 NAT 之下
  • 有一台阿里云 ECS 做跳板机,安装有:
    • Wireguard(用于和不同的远程设备连接)
    • nginx(通过 stream 模块向公网转发端口)


在此之前,我经常会使用 RDP 来连接我的远程工作站。启动 RDP 服务不需要复杂的服务端设置,在网关机上也只要简单转发一个端口到公网即可。

其实 RDP 对于一般的应用程序是没有问题的,但是完全不适合打游戏:


我的工作站配备有 GTX 1650 Super,因此可以借助 GeForce Experience 的游戏串流(Streaming)功能和 Moonlight 客户端,轻松搭建自己的云游戏服务。

总体来说,在有合适的网络条件下,Moonlight 可以提供足够好的游戏体验。但是它也有一些蛋疼的问题:

  • 配对时必须在远程主机上输入四位数字确认
  • 一旦连接过 RDP(因为我平时还要工作!),串流服务就会停止工作
    • 所以,上述的配对过程也不能在 RDP 进行,必须使用其它的远程桌面服务(如 Teamviewer);
      同时,每次连接过 RDP,都需要用 Teamviewer 再连接一次。


Parsec 是一款与 Moonlight 类似的应用,但是解决了 Moonlight 的很多痛点:

  • 验证是基于他们自家帐号系统的(比在远程主机确认配对方便多了!)
  • 连完 RDP 以后可以直接连接
  • 灵活的连接方式,可以自动视情况通过局域网、Wireguard 等方式连接到服务端
  • 支持 A 卡(虽然我现在用的是 N 卡)

不过,你只能使用 Parsec 他们家的帐号系统,而且他们的客户端比较黑箱。我不是很在乎就是了……

让 GPU 一直工作

我在淘宝上随便买了个 HDMI 锁屏宝,解决了这个问题。

不过,也可以试试 虚拟显示器



服务/特性 RDP Moonlight Parsec
支持显卡 N/A Nvidia 各种
配置难度 中等
首次连接 输入本机登录信息 在服务端完成配对操作 输入 Parsec 登录信息
流量消耗 较高 中等
图形渲染位置 客户端 服务端 服务端
使用 RDP 后再连接 N/A 需要其它远程桌面工具再连接 可直接连接
开源软件 客户端开源



不过,如果你没有长期使用 Windows 远程桌面的需求,而且只想玩一些大众游戏,那么类似于 网易云游戏平台腾讯云游戏 等服务都是可以考虑的。

Update: 如果你是原神国服玩家,还可以考虑一下官方的云・原神,不过看起来只能在手机上玩。


别人花 648 抽优菈,我花 648 让游戏跑起来。


黑狗布雷特's avatar


Horo's avatar

Google 的 FLoC 是个糟糕透顶的主意

翻译自电子前哨基金会 (Electronic Frontier Foundation) DeepLink BLog 的 Google’s FLoC Is a Terrible Idea : , 原作者 Bennett Cyphers

EFF 网站的内容以知识共享 署名 4 …

's avatar


在 RSS 中无法显示完整的格式,不同的 RSS 阅读器所显示的内容也可能会有很大差距。为了能够获取到完整的内容,最好还是点开网页链接看。




还是来给一些例子吧。被新海诚导演的电影《你的名字》,有一位声望很高的导演、惊艳的宣传海报,并且在我观看之前还看到了非常多的来自其他人的夸奖。其结果就是对它的期望漂浮到了天上。然后在我观看完毕后,感觉到的却只是“不错”,这部电影真的值得那么多赞扬吗?这个问题不好回答。剧情的发展感觉有太多的巧合了,主角们的经历感觉更像是编剧为它们铺好的道路(当然,实际上也确实是这样),而不像是使人信服的冒险。所以在我刚刚看完电影的时候,只给了它 3.5/5 星的评价。我感觉这就是“被高估了”所代表的意思。但仔细想一想,《你的名字》是个精彩的电影吗?大部分人应该都会认同,我也在仔细想了想之后认同了。


有一部神奇的作品,恰好因为这个神奇的原因使我喜欢,它是《One Room》第一季。是一部共有 12 集,每集 5 分钟的动画系列。它展现的是 3 位少女(不是在一起出现的,而是每位各有属于自己的 4 集)对着镜头说话,像是在和观者在一起渡过时间一样。动画的创作者们称它为“第一人称动画”。这个点子听起来很神奇,不过从上面的描述应该可以看出,应该会是一部看起来会令人感到尴尬的作品。我显然没有对它抱有很高的期望,既然只是动漫美少女对着沉默的镜头单方面说话。只是它反正只有 5 分钟,为了这个神奇的点子,即使它就是很烂,每周浪费了 5 分钟也可以接受。前 2 位角色(前 8 集)的故事同期望中一样,有一点趣味但实际上还是比较无聊,不过没有关系,反正每集只有 5 分钟,再继续浪费 4 次这样的 5 分钟也没有关系嘛。每一位角色有着属于自己的角色歌,被作为片尾曲使用,这些歌曲还可以。


以下 1 个段落包含对 One Room 第一季第 9 到 12 集的剧透。尽管你可能不觉得这是一个值得担心剧透的作品,但我写文章的方式应该已经表示了这最后 4 集是出乎意料的。也许可以值得看一看,反正每集只有 5 分钟嘛。 点击此处显示剧透

这一次,歌曲的感觉完全不同了。它现在有了灵魂。在它之后的故事尽管很简单,但却出乎意料的引人入胜。我无法相信自己居然在被这样一部看起来很尴尬的动画打动。如果《One Room》在一开始宣传自己是一系列感人的故事的话,那它一定不会有这样的效果。



Valve 的电子游戏系列《半衰期》是一个有趣的例子。它的第一部游戏(1998 年)是一部经典,震惊了许多人,甚至也包括了在约 20 年后才玩它的我。然后它的续作《半衰期 2》在 2004 年众望所归地发布。人们有很高的期望,这个时候应该也是最害怕它实际上令人失望的时候了。但 Valve 显然又一次震惊了它的观众们,展现了许多新奇的东西。而如果它们只是维持了现状的话,这些新奇的东西一定不会出现。我记得有见过一篇对游戏团队领导的采访,他说,震惊玩家与取得突破正是他们所努力争取的。开发者们对着自己期望很高,才得以匹配消费者的期望而成功。但是这样高的期望——依我看过各种信息所理解的——也是它们的弊端。《半衰期 3》的开发渐渐停止,最后也一直没有完成,大概是因为开发者与消费者们对它的期望都太高,超过了实现的能力吧。

所以……嘛,我也不知道该从这篇文章的讨论中总结出什么,只是想突然无端地说一下。所以那么,希望这篇文章有一点价值,感谢阅读吧。 🐁

题图取自 One Room 第 1 季第 9 集的 0'23" 。

除特殊说明以外,本网站原创内容采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。转载时请注明来源 FiveYellowMice 的博客 ,以及原文链接

's avatar

Subverting Expectations

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.

I think a huge factor in how good a piece of artwork feels to me is how much it subverts my expectations. The "artwork" in question can be literature, illustration, music or perhaps more other types.

If a work sets up a high expectation from me, by either making really good marketing materials or its author(s) is/include someone who has produced good works before, it will need to reach or overachieve for it to not be "disappointing". It can still be objectively really good, but for me, because it is comparatively worse than the expectation, it may feel like as if it’s actually bad.

Likewise, if I don’t have much expectation for a work. For example, a low-budget work from someone I have not heard of, or works that does not appear to be serious (like a comedy or a meme). As long as it turns out to be better than that low expectation, I will feel like it’s amazing. Even if objectively, it’s so-so at best.

I want to give some examples. The movie Your Name directed by Shinkai Makoto. It has a highly-reputed director, stunning marketing posters. And before I watched it, countless praises had been seen from my peers. The resultant expectation was floating to the skies. After I watched it, I felt it was "not bad", but was it worth the hype? That was hard to answer. The plot progression felt like there’s too many coincidences, the main characters' experiences seem to just be a road laid down by the writer (which to be fair, it obviously is) rather than a believable adventure. So right after I finished the movie, I gave it 3.5/5 stars. I felt this was exactly what "overrated" means. But let us think rationally, is Your Name a good movie? I suppose most people would agree, and I agreed after I thought rationally.

On a similar vein, also an anime movie, Sword Art Online: Ordinal Scale, but this one faces the opposite scenario. The Sword Art Online series is not known for it’s depth. In fact, its story is considered really shallow and only good for "thrills" by critics. To be honest, I like Sword Art Online, I mean, I like plain thrills without much depth. So I watched the movie, and it offered me exactly what I expected: thrills without much depth. It actually overachieved a little because the movie has flashier visual effects than what’s in the TV series which I was used to. I somewhat felt better after watching this than after watching Your Name, but I would not put the 2 works on the same level.

There is a work I surprisingly liked because of this weird reason. That’s One Room season 1. The 12-episode 5-minute-each anime series that shows each of 3 girls (they don’t appear together, each one has 4 episodes dedicated to one) talking to the camera as if she is spending time with the viewer. The makers call it "first-person anime". It felt like a novel concept, but as you may guess from its description, it’s going to be an awkward anime to watch. I obviously didn’t expect much from stereotypical anime girls talking to a silent camera. I just watched for the novelty since even if it was terrible, I only lose 5 minutes each week. The first 2 characters (8 episodes) was as I expected, kind of sweet but still boring, but it only lasts 5 minutes so I didn’t mind wasting those 5 minutes 4 more times. Each character has a song sung by the voice actor and used as the ending song, the songs are ok.

Spoiler Alert:

The following 1 paragraph contains spoiler for One Room season 1 episode 9-12. Although you may not think spoilers for this work is worth worrying about, the way I wrote has indicated that those episodes are unexpected. They’re probably worth a watch, they are only 5 minutes each anyways. Click here to show spoilers

This time the song felt vastly different. It has a soul now. The story behind it, while simple, was unexpectedly engaging. I could not believe I was moved by a show this awkward-appearing. If One Room had marketed itself as a collection of nice stories, it would not have this effect.

This behavior of mine, using how are expectations are subverted to decide whether to like an artwork, is probably irrational. It’s definitely better if I can view works more objectively, isn’t it? I assume this is not weird and many other people do the same.

There may be situations where this subversion-seeking behavior is beneficial, if many people have it, I guess. Like maybe it gives more chance to start-up authors than to established ones, which perhaps encourages innovations. Although it probably makes life harder for people under high expectations.

The video game series Half-Life by Valve is an interesting example. The first game (1998) is a classic, people were astonished by it, including me who played it at about 20 years later. Then the long-awaited sequel Half-Life 2 came out in 2004. People then had high expectations, and that’s probably when the fear of it being a disappointment was the highest. But Valve had apparently once again astonished the audiences by introducing so many new things that would definitely not exist had they been keeping their status quo. I remember reading about an interview to the games' leader, and he said astonishing people and making new breakthroughs was exactly what they worked hard for. The developers expected very high from themselves to match the expectations from consumers in order to succeed. But this high self-expectation was, as I understand it from reading various information, also their detriment. Half-Life 3’s development slowly halted and never completed, probably because both consumers and developers expected so high that they were unable to fulfill it.

So…​ Well, I don’t know what to take away from the discussions in this article, I just wanted to spontaneously talk for no apparent reason. So I hope this is informational, and thanks for reading. 🐁

The heading picture is taken from 0'23" of One Room season 1 episode 9.

Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

Phoenix Nemo's avatar

FAT32 与 FAT32 的不同


HY's avatar


这么多年策略性游戏玩下来给我印象最深的不是某些大公司的作品,而是一款独立游戏《Into the breach》 […]
ホロ's avatar

用 Hyper-V 玩些小花样

从前面那篇 Hyper-V 和 OpenWrt 搭建软路由 开始咱就开始在 Windows 上用 Hyper-V 了。 然后场面逐渐失控……

这篇文章就是把咱这几天遇到的各种问 …

's avatar









もう一つは、韓国語を勉強してることかな。日本語で韓国語のこと言ってるのは確かにおかしいだが、言語はいろんなどこ似ている。例えば、数字の言い方も二つある、一つは中国語の発音から来た(il, i, sam, sa, o)、もう一つは自ら発明した言い方で、主に数える時に使う(hana, dul, set, net, daseot)。もう一つは曜日を使ってること、例えば、日曜日は il-yoil(ilは日で、yoは曜)。文法も少し似ているような気がしますが、あまり文法について勉強してないので割愛。

一か月前くらいで、ピアノを買ってました、ヤマハのP71です。グリーンハンド、初心者なので、それに適するではないかな。主に、音ゲーやる時間があるなら、一つ楽器でも勉強した方がいいではないかと思った。以前やってないのがちょっと悔やまれるが、それはそれでもっと自由の幼年を過ごせたので、どっちがいいか簡単に言えない。興味というのは、いつでも、当時のやりたいことを重んじることだと思う。興味が変わればそれに従い、現在を楽しめること。 Carpe diem といって、同じ名前のゲームが steam にあります。遊んではいないので、おすすめできないが。

思いついたことはこれくらい、すみません、言い換えることあんまり出来ていなくて、これから本番で、最後のチャンスです。しかし、雑談なので、どうやって括ろうかを悩む。最近、村上の UT(一応ユニクロのシャーツと説明しよう)出てるんですけど、すぐに完売してる。カフカのシャーツとか。20日現在 1q84 以外ほとんどなくなって、 Ebay で二倍の価額で転がっている。ノルウェーの森のカバー色柄のシャーツも全部売れたのに(英語版は赤と白と黒で、日本語のと違う)、なぜ 1q84 は売れていない。みんなもっと 1q84 を買おう、と世界の中心で叫びたいが、自分一服も買っていない。配送料が高いのでやめた。

これをブログにアプロードしようとした時、WSL がネットに繋がらない、めんどい。

's avatar

Making a Custom Smart LED Clock

It's not possible to display full formatting in an RSS reader, different RSS readers may also display drastically different layouts. Therefore it's recommanded to read the article by clicking the web URL.

The finished product displaying a time of Tuesday, 16 March 2021 13:37:07

After around 2 years, my first (presentable) DIY project is finally complete! It’s a device with a 16x16 LED matrix display, which is primarily used to display time. It’s also WiFi-connected, syncs time via NTP, can have 3 alarms, and potentially do more interesting stuff with its internet capability.

So, this post will document the process of me making this thing. I didn’t have many mishaps, the process is relatively smooth, and the reason it took so long is mostly me procrastinating.

For a lack of name ideas, I used a random pet name generator online, and it gave me the name "Baby Tiger". This is probably not the best name for a digital clock (it sounds like a very generic name for a cat), but I have no better ideas. Name suggestions are welcome.

The Electronic Components

I guess it’s because of watching YouTubers like GreatScott!! and DIY Perks piqued my interest and I wanted to make something of my own. And a clock with LEDs sounded like a fairly conservative thing to make that would not present much trouble, since similar hardware and design patterns seems to be done by others a lot. So with this thought, and the knowledge from YouTube videos that a kind of microcontroller⁠[1] called the Arduino would be suitable for controlling LEDs, I bought an Arduino Nano knockoff⁠[2], breadboard⁠[3], jumper wires and RGB LEDs.

The first RGB LED I got

This RGB LED is a package of 3 LEDs of colors red, green and blue. I need to power each color individually in order to display different colors. I made it to work, but there were big catches:

  1. I can’t adjust the brightness of each color components directly, so I can only have the primary and secondary colors, plus white and black (off). I had to use PWM⁠[4] in order to make fancier colors.
  2. The weirder problem was this RGB LED is unlike just 3 LEDs in parallel, the red, green and blues LEDs can’t be on simultaneously, which means I had to do PWM faster and the software needs to be a little more complicated.
  3. Each LED consumes 3 pins for the 3 color components. I was making a clock, so there eventually needs to be a large quantity of LEDs connected before displaying anything resembling time. There aren’t that many pins on an Arduino Nano. I learned about shift registers that can "expand" the number of controllable pins, but then I had to do PWM with software which felt very complicated. Shift registers will also make outputs slower, combined with the issue above, I was afraid that the speed of an Arduino Nano wasn’t fast enough.

I complained about this online and prepared to buy some shift registers to try nevertheless, then someone online suggested me to take a look at a more sophisticated RGB LED called the WS2818B. I looked it up, and it seems like a very popular choice of RGB LED. With a WS2818B, it will only need a single data pin to receive the color data, and will retain the color without the controller doing PWM. It also appears that those things can also be chained so I can have theoretically as much LEDs as I want, using just a single pin to transmit color. These features compared to the original LEDs, sounded like driving a car compared to crawling. I was carried away, and thought of expanding the project into something fancier.

I won’t make any regular old digital clock, I wanted make it to have a matrix of LEDs that functions as a screen, so that it can display more interesting things than just time. Coincidentally, I saw a product listing of 16x16 WS2818Bs pre-made into a matrix, which sounds like exactly what I want, saves me the trouble of laying out the total of 256 LEDs and soldering them.

The LED matrix, taken from the product listing web page

As I researched, the device won’t function with just a microcontroller and 256 LEDs. It will also need these less important, but still necessary things to be a decent clock:

  • A power supply. Both the Arduino Nano and the WS2818B matrix wants 5V power. According to an article I read online, a single WS2818B LED can draw 50mA of current, which sounds small. But I have 256 of them, so it multiplies to 12.8A. I couldn’t find a mains power supply that converts to 5V and is rated for more than 12.8A. What I was able to find were a mains to 12V power supply that can carry that much power, plus a converter that converts those 12V into 5V. The 12V-to-5V converter can only carry 10A though, but according to the article mentioned above, 50mA is just a maximum, so as long I don’t run it at full brightness and every pixel being white, it should be fine.
  • 5.5mm DC power barrel jack. This allows the 12V power supply to connect to the input of the 12V-to-5V converter.
  • DS1307 RTC module. This module keeps the clock running in the background, even when power is disconnected, so I would not need to set the time every time power is reconnected. I suppose it may also a bit more accurate than counting the time with the Arduino’s internal CPU clock.
  • More wires and breadboard.

At the same time, I came across something called an ESP8266. They say it’s similar to an Arduino, but it can connect to WiFi. I then excitedly decided to use that as the central microcontroller and ditched the now-dumb-looking Arduino Nano, and started daydreaming about the potentials of having internet access: I could synchronize time from the internet, and possibly displaying messages and notifications, even stream videos! (Yes, I know, playing a video on a 16x16 screen is extremely impractical. It could probably play Bad Apple though, which sounded pretty cool to me.)

Now the project has become more ambitious, I started to think of even more features that I could add to this clock.
Displaying current temperature? Yes, let’s add a temperature sensor.
Having the ability to sound alarms? Yes, let’s add a buzzer.
Buttons too small and ugly? Let’s use the 4 Cherry MX key switches⁠[5] I happened to have lying around instead of regular small tactile buttons, and use some custom key caps which I’ll worry about later.

So here are the aforementioned things I ended up having inside the clock, plus some other details:
(This photo was taken after the whole thing has been built, it obviously didn’t look like this at that stage. They were stuff plugged onto breadboards, I have a photo of that later in the article, but that photo wasn’t suitable for explaining what the components are.)

① 12V-to-5V DC converter
② 5.5mm DC barrel jack
③ Arduino Nano
④ ESP8266
⑤ DS1307 RTC module
⑥ BME280 temperature sensor
⑦ Buzzer
⑧ 5V-3.3V bidirectional logic level shifter, this allows the components operating at 3.3V (ESP8266 and BME280) to communicate with the components operating at 5V (others) without blowing up
⑨ The board that holds the 4 keys of the device, a.k.a. key board, not to be confused with a computer keyboard
⑩ Where the key board connects to the main board
⑪ Where the 16x16 LED display connects to the main board, the display is on the other side (front)
⑫ Unused connections of the display glued to the box so they don’t get in the way
⑬ Unused connections of a reset button, I decided later that this is unnecessary for normal use
⑭ Unused hole where the reset button was supposed to be

Why do I still have an Arduino Nano in the picture above? Didn’t I say I ditched it? Well, it turns out that controlling 256 LEDs with 1 pin requires a lot of work from the microcontroller. It requires very precisely timed signals so the microcontroller can’t do anything else during signal transmission, which is bad if I want to have WiFi connected because it requires some WiFi stuff to be constantly done, I also want time for my own code to run. There are tricks to use dedicated hardware to transmit these signals, but they either consume too much memory, or occupies the pin I need for other purpose. So I put the Arduino Nano back, and programmed it so that when the ESP8266 wants to update the display, it transfers the display data into the Arduino Nano, the Arduino Nano then does the time-consuming job of transmitting precisely-timed signals, while the ESP8266 carries on doing more important things. Was is a waste to put a whole Arduino just for this singular purpose? I suppose so, but it was the easiest way of solving my problem.

The Software

After I have got those components on the breadboard and connected (it didn’t look like the picture above, it was just on the breadboard), I started programming the software.

First, I decided the interactions I could have with the clock. It will have 4 buttons: , , , . When it’s powered on, it will display the current time (I’ll call it "clock mode"), pressing will turn off the display, and pressing will bring up the main menu, where the various additional functionalities (like alarms and settings) can be selected. and changes the selection, confirms the selection, and returns to an upper-level menu or the default clock mode.

Flowchart of basic interactions of Baby Tiger

While making the clock mode, I realized that there isn’t enough pixels to display everything about the current time. A 0-9 digit takes at least 3x5 pixels, each 2 digits needs to have 1 pixel between them so they don’t fuse together. It ends up being a little challenging. I have to use different colors for different parts of the time for them to look distinct. I made a separate screen to show the year number and month name when is held down. Pressing will on the other hand switch the time display into temperature/humidity and temperature/pressure obtained through the BME280 temperature sensor.

Examples of the different screens in clock mode

At this stage, I was pretty happy about what I have accomplished, so I posted its photo on social media.

What it looked like after completing the clock mode

Afterwards I made the main menu (and by afterwards I mean 1.5 years later, yes I procrastinated for that long). It shows the name of the menu item at the bottom half of the screen. Right now they are Alarms, Games and Settings. Since the screen is so small, only one option is shown at a time, and the text scrolls from right to left.

Main menu showing the "Settings" option

The settings menu consists of these options:

  1. Time: Setting the current time manually. There aren’t much to be noted, except writing the UI interactions were kind of tedious. Anyways, it works.
  2. Timezone: Because I use NTP to synchronize time with the internet, and NTP only cares about UTC, I have to take care of the local time offset locally, which is changed from here.
  3. NTP: I originally planned to make an On/Off switch for NTP, but didn’t ended up bothering, so right now it just shows the last synchronization time, and if there is an error synchronizing.
  4. WiFi: Setting the SSID and password of the WiFi network to connect to. Since the clock does not have a keyboard to type those things, I have to do it the around-about way: Let the ESP8266 make a WiFi access point for a phone to connect to, present a web page for inputting the SSID and password, receive them from the phone through HTTP, and finally try to connect to the network. Writing code for this process was the most tedious of them all (I wrote an HTTP server from scratch!). It eventually worked, kinda, although I don’t dare looking at the spaghetti code I wrote ever again.

In the alarms menu, I made 3 alarm slots. The enabled alarms are green, while the disabled ones are red. Since I don’t have enough vertical pixels to display 3 rows of timestamps, the last alarm will have its last bit cut off, and everything will get shifted up when the last alarm is selected. Going into an alarm setting (by pressing ) will let me change the hour, minute, on/off, whether it repeats on each day of of a week, and which music to play when the alarm triggers. It ended up looking pretty good, if I do say so myself.

Alarm setting on Baby Tiger. There are 3 alarms, the 1st and 3rd alarms are disabled, the 2nd alarm is enabled and set to 7:15, repeats every Friday, music set to Katyusha.

I originally planned to put some games on there, but they aren’t exactly necessary, and seemed like a lot of work, so in the end I didn’t write any. The "Games" on the main menu is just the demo music player that plays Senbonzakura.

I mentioned 2 musics that’s on Baby Tiger, Senbonzakura and Katyusha. They aren’t really musics though, as I could only play square waves from a ESP8266. It can’t play regular music file, nor does it play MIDI. These 2 tracks are just notes I typed into the code manually. (I really should write a script to convert from MIDI, if I really want many different music to select from. Doing this by hand is pretty time-consuming.)

And then I felt like the software was done enough. So the next step is to figure out how put them in a good-looking enclosure. But before that, I got to move the electronic component out of the wobbly breadboard, onto a more sturdy circuit board.

The Circuit Boards and Keys

So the plan was to have 2 separate circuit boards connected by a ribbon cable. One was going to be the main board, holding most of the stuff; the other was going to be the key board, holding the 4 key switches, as well as the capacitors and resistors for debouncing.

Let’s talk about debouncing⁠[6]. There are 2 ways of debouncing, hardware and software. I chose hardware because the article I read to learn about the topic said "It’s a hardware problem, so it’s best to be solved with hardware." and I agreed that hardware feels more elegant. The problem is, I don’t have an oscilloscope, so I just had to guess how long and how often the buttons bounce, and in turn had to guess the capacitance and resistance values in the debouncing circuit. The guesswork was a weird experience. The idea of hardware debouncing is to let the capacitor charge or discharge slowly when the button is pressed or released, so even if it bounces a little, the voltage would not change too much as to let the microcontroller think as extra button presses. That means the bigger the capacitance, the bigger the resistance, the slower the voltage changes, so the less likely for the bounces to have an effect, with the trade-off of the button press feeling more laggy, right? Well maybe my understanding is wrong, because changing to bigger capacitor apparently make the bouncing worse than having a small capacitor. Changing resistors could also make it worse – or better, it feels completely random, and the ESP8266 is so weird that some pins seems to work the best with different valued resistors than other pins. I still couldn’t understand why that is, maybe the answer will be more apparent if I had an oscilloscope. Eventually though, I figured out a circuit that worked well enough.

Schematic of the key board

I didn’t plan to draw and order a custom-made PCB, I thought just soldering stuff onto a perf board⁠[7] should work well enough for my purpose. I was wrong.

I started out making the key board since it’s the simpler of two. Then I realized what appears to be simple on a YouTube video may not be so simple if I do it myself. Those YouTubers had experiences and I didn’t. The process of realizing the circuit diagram above was slow and painful. I couldn’t keep the components in place before soldering them, it ended up resulting in none of the key switches are seated nicely, they tilted in all different directions. It’s really unideal, but what’s been done is done, I can’t do it again unless I buy new key switches. It was also difficult to make the wires connect to where I want them to connect to while not touching where I don’t want them to have contact with. After I finished making the key board, I concluded: There is no way I can do this manually for the much more complicated main board.

Schematic of the main board

Defeated, I sat back to the front of the computer, and decided to draw a PCB with KiCad. I didn’t know how to draw PCBs, but luckily it didn’t take too much time to learn with the help of guides found online. And I was glad to not go down the manual perf board soldering path, because the PCB I ended up drawing looked like this: They are all necessary connections.

PCB design of the main board, red lines are traces on the front layer, green lines are the traces on the back layer

I sent the PCB design to a company that makes custom PCBs for individuals. The board itself was not very expensive, costing US$ 4, but the shipping though, they cost $14 and was estimated to take about a month. The DHL express shipping costed $21 but is estimated to take less than a week. I chose the latter. The slow shipping was already that expensive, so it felt justifiable to buy the 1.5 times as expensive but 4 times as fast shipping method.

It took less than a week to arrive, as expected from DHL. I then soldered all the remaining components, completed with significantly less sweat than when I made the key board.

Now comes the moment of truth: If I drew the PCB wrong, then things will not work and I’ll have to spend $25 again. Or worse, I’ll blow up components. Luckily nothing seemed wrong as I plugged in power – a successful milestone.

The photo I took when posting on social media

The Enclosure

It was the time for the non-electronic part: making an enclosure for everything to sit nicely and look good in. In my imagination, I figured that the shape of the enclosure should be a rectangular box, with a shallow depth, like a picture frame. The front has the screen, covered with translucent acrylic, and behind the screen are all the control circuits. I figured that wood is probably suitable as its main building material. It’s heavy enough so the device won’t tip over, and it also sounds like a very commonly used material.

I had no experience in woodworking. I was a bit overwhelmed by all the information I needed to get started without spending too much money. There were questions like what suitable types of wood are, how to make right angled joints that aren’t easily broken, what types of glues I need, how to cut square holes, how to make a detachable backplate. A lot of the problems weren’t hard, they seemed to be pretty easy with powerful enough tools. But I didn’t want to spend that much money on powerful tools. They were really expensive. OK, they were not that expensive if I was going to use them all the time, but I couldn’t think of when the next time I use them will be, and investing hundreds of dollars into tools I could only think of using once is definitely not worth it.

To demonstrate my point above, let’s talk about how I wanted to cut 20x20mm square holes onto a piece of wood in order for the keys to poke out. What options did I have? The tools I had were an electric dill (which I bought from Ikea a long time ago) and a hacksaw. An electric drill can only dig round holes, how do I make square ones? The answer I got from the internet was to use a "router". Well, I did have a router to use, it even had 1 Gigabit Ethernet, but it’s apparently not the "router" I need to cut square holes. The router for cutting square holes was something I didn’t have, and costed quite a lot. And apparently, a router was not enough! To assist the router into cutting straight lines, I would also need a "template", and templates seemed to be quite a deep rabbit hole.

I guessed I could first drill a round hole that the diameter is the same as the side length of the square, and then use a saw to cut the corners. Like the left halt of the image below. But I could not find affordable drills bits that are 20mm in diameter, and I could not find saws that would fit into a 20mm round hole even if there were 20mm drill bits.

Circular holes are drilled first, then the material in shaded areas are removed by other means, creating square holes

So sawing were out of question, what else? Maybe using a file instead of a saw? It’s probably going to be more time-and-strength-consuming though, but I was able to find small enough files for not-too-high prices. As for drilling the round holes, I guessed using the largest drill bit my drill could hold (8mm), and drilling multiple of them should suffice. As seen of the right half of the image above.

After painstakingly researching every potential problem I though of (they were not enough), many WikiHow-reading and YouTube-watching. I settled on a design that balanced ease-of-making and looked passable.

The mockup 3D model. From left to right: front, back, back with backplate off

I originally planned to use wood for the square frame (yellow in the 3D model), the display support (orange in the 3D model, put there for the display to be glued on) and the back plate (also yellow in the 3D model). I didn’t expect regular wood to be so expensive though! So I turned to the alternatives, which are still at least made of wood fibres. Plywood, which are made by gluing thin pieces of wood together, and somewhat cheaper. And MDF, which are made by compressing wood fibres (I feel like they are similar to paper, just thicker and stronger. MDF also disintegrates with water, just like paper does.), and quite a bit cheaper. I contemplated on which to use, watched a few videos explaining the differences. It turns out plywood seems to be better because the dust is less fine and thus less harmful for the lung, and does not disintegrate with water. But I still turned to MDF since it’s quite a bit cheaper. It shouldn’t matter much for my project, I guessed.

So I went shopping for tools and materials. I bought:

  • A piece of 12mm thick MDF, very large, much more than I needed, but they don’t sell smaller ones.
  • 2 pieces of 3mm thick MDF, for some reason they had small piece for 3mm, and the size was just enough for what I needed.
  • A large file.
  • A set of small files, they only had small files in a set and not individually.
  • A handsaw.
  • Wood glue.
  • 4 clamps.
  • A square stick of cheap and soft wood for support while gluing and drilling.
  • 8mm drill bit.

Then I started cutting wood into the pieces shown below. The reason I used this shape with protrusions and slots for the side pieces was to increase the surface area of the joints. The 4 side pieces joins end-to-end, and the protrusions slots into the slots. I am not sure if this was worth the effort since this thing doesn’t need to be that strong, but everyone online seems to emphasize how important it is to increase the surface area of joints. And no one on YouTube seems to make a right angled joint by just sticking one end of a piece onto the side of another (a.k.a. a butt joint).

Wood pieces I cut. Black shapes are 12mm thick, orange shapes are 3mm thick. Units are in mm.

I tried to make the slots on the side pieces by sawing off as much material as the saw can reach and filing down the remaining. It turned out the large file I bought wasn’t suited for the job. I filed for a long time but little material were visibly removed. The small files were obviously going to be worse, since they are even finer. Now making the slots were already this hard with the files I have, making the square holes in the later steps were definitely going to be harder. So I went back to the hardware store, returned the small file set, and bought a set of rougher files. Filing was still slow with the rougher files, but at least my work could have visible progress.

Making the square holes was as painful as I imagined. Drilling the 8mm-diameter circular holes were easy with an electric drill. Removing the rest of the material in the square hole area was a long and tiring process. I filed and filed. Working from afternoon to evening to early night. Had my neighbour complaining about noise. The "square" holes were irregularly-shaped. But I was really tired, so I decided it was good enough and stopped for the moment. It later turned out the holes were not large enough to fit the keys, so I had to enlarge them a little after gluing.

The "square" holes as seen on the finished product. Don’t mind the key caps, I’ll talk about them later.

After cutting the 4 side pieces, I glued them together to make the frame. Since the protrusions and slots on the ends of the pieces were not that perfect either, the wood glue did not really make great contacts. But they still held up, which was good enough. I was really scared of the frame turning out to be not square (slanted into a parallelogram), but luckily it was square enough, after waiting 24 hours which was how long the label of the wood glue bottle said I needed to wait.

I also cut the display support and backplate, and glued the former with hot glue.

Since I originally planned the box to be made of wood, I didn’t plan to paint them. Wood grain patterns seemed pretty nice. But now I’m using MDF, I had to paint it so it is not MDF-brown-coloured. The front panel was supposed to be a black translucent acrylic in order for the screen to appear black where pixels were not lit. So I guessed I might as well paint the whole thing black. But also because it was MDF, which disintegrates with water, many paints cannot be used on it, including acrylic paint which was the one of the only paint types I could get in small quantities (I guess most paint containers were designed for people who paint walls and stuff. Buying 2kg buckets of paint just to paint this small thing seemed super not worth it for me.). I went to research online again, and concluded that I should use spray paint cans.

I went to the hardware store once again to buy the spray paint cans. The spray paint cans’ label said I need a primer as well, so I bought a can of flat black paint and a can of primer. Back home, I laid down paper of the floor of my balcony and started spraying. It smelled pretty bad even as I did it on the balcony. It was also a long process, since instructions online as well as the label said I need to wait 15 minutes between each coat. So after painting a few coats of primer and more coats of black paint, it became dark once again. Fortunately I finished before the sky was as dark as I stopped being able to see clearly.

Remember the hot glue I used to secure the display support? One corner came loose after the spray painting. So I added wood glue as it should be stronger than hot glue.

At this stage, I still did not have a way to put the back plate on. It needs to be removable as I need to access the electronics inside. I first thought of using screws, but people said screws in wood becomes loose after unscrewing. A friend suggested using magnets, which I guessed was a good solution.

I bought 5mm diameter neodymium disc magnets online. 5mm is a size that can fit into the sides of my 12mm thick side panels, and also a size I have a drill bit for. I drilled a hole on the middle of each of the 4 sides and placed magnets in them. I also taped magnets of opposite poles onto the corresponding locations on the back plate. There were more problems:

  1. My hand were not still during drilling. So the holes were not in the exact centers of each sides. They drifted a little, enough drift for me to having to adjust the locations of magnets on the back plate. Since the drifts were irregular, I had to lay the frame onto the back plate, poke the magnets into each of the 4 holes, then carefully lift the frame, leaving the back plate with the magnets on the correct location.
  2. I put the poles wrong! Same poles of magnets repel each other which was the opposite of what I want, so I had to take them off, and repeat the step above.
  3. 1 magnet pair on each side turned out to be a lot weaker than I expected (Aren’t neodymium magnets supposed to be insanely strong?). I tried drilling the holes deeper and adding more magnets. Eventually I put 4 magnets on each sides of the frame and the back plate, a total of 32 magnets.
  4. Hot glue could not make a strong bound for gluing neodymium magnets to MDF. I had to buy yet another type of glue: the 2-part epoxy. It came off once more, and had to try again with thicker layers of the glue.
  5. One magnet on one side of the back plate did not get glued well, and got broken off and stuck inside the corresponding hole on the frame. It seems to have stuck pretty tightly, so I didn’t bother getting it out and gluing it again. It became a feature.
Finished back plate, this side faces the inside so I didn’t bother painting, and the hole is for plugging power

Before putting the front panel on, I glued the display onto the display support, also with epoxy glue.

I wanted the acrylic front panel to be translucent, not semi-transparent, in order to diffuse the light from inside so I don’t see the pixels when they are off. The 200x200mm acrylic I bought from the beginning was translucent when the protective films were on, but I knew it would just be a semi-transparent black when the films were peeled off. Leaving a protective film on the finished product is definitely ugly, so I needed another light diffusing layer. I thought tracing paper would be fitting, but they were actually quite expensive and the stores I went to had them sold out. I found a piece of frosted plastic in alternative, which I think actually worked better than if I had used tracing paper. I glued one side of the acrylic to the cut frosted plastic with epoxy glue. And then with the plastic facing inside, I glued them onto the frame. It came off once and I had to glue again.

By the way, 2-part epoxy glue smelled a little gross, the B part smelled like fish for some reason.

With all these stuff done, I could finally put the electronics in. I glued the key board, main board, the 12V-to-5V converter, the DC power barrel jack in this order, with a combination of hot glue and epoxy glue. So far they have stayed in place.

Inside the enclosure after all components have been put in. It’s the same picture from before, ignore the numbers.


Plugging everything in, now it’s the most important moment of truth. If this somehow ended in smoke, I would be very upset. It shouldn’t have any problems though, as I have tested it in every stage. I was nervous, my hands trembled as I plugged in the power, my heart apparently stopped beating for a second before the display came on. But finally, my worries turned out to be needless, as the product of my hard work blinked each second with accurate time on the screen.

I called it a success.

I posted the completed photos on social media once again, and people congratulated me.

The final results. Left shows the time, right shows the temperature and humidity.

Well, it’s not flawless though. First of all, the whole thing looks totally handmade and imperfect. The surface finish is rough, and marks left by dried glue is all over the place. Some unpainted areas shows from the outside. The back plate does not attach well not only because the back of the frame is not flat at all, but also because the glue around the magnets on the back plate formed a chamfer, preventing them from going all the way in. Unlit pixels still shows though the front panel a little, though I guess it’s acceptable. And not to mention the ugly-shaped corner joints and holes for the keys. Also there’s an unoccupied hole on the left side that was supposed to house the reset button I ditched.

Second of all, the temperature measurement is a lot higher than the actual room temperature. The reason is probably the other components are dissipating a significant amount of heat so the inside becomes hot. I didn’t foresee this, I thought this thing wasn’t going to emit enough heat to affect the temperature measurement, I was totally wrong. The amount discrepancy is unacceptably high (like, about 8 ℃). Also because all the other environmental measures (humidity and pressure) are based on the temperature measurement, they are all unusable. I took off the back plate so it’s a bit better, but, like, there’s no back plate then.

It’s also not really finished. I used Cherry MX key switches, so I got to use key caps designed for a computer. But my buttons are , , and . On a computer keyboard, there are left and right arrows, but obviously no cross and circle. I could use X and O letter keys for and , but they are differently shaped from each other and the arrow keys. I saw online that epoxy resin⁠[8] is something people often use to make custom key caps, so I guess I could do that as well. But that’s another deep rabbit hole I’ll go into later in time. Meanwhile, I just randomly took 4 same-shaped key caps from a pile. They’ll eventually get replaced.

The software also doesn’t have much functionality. The excitement of having WiFi when I switched to ESP8266 did not do a lot. The only thing that currently uses WiFi is syncing time via NTP. I guess I might want to expand the software more later in time, adding countdown timers, stop watches, displaying notifications, and maybe actually playing Bad Apple.

Throughout the making of this project, it made me realize that DIY is probably not going to be a money-saving options oftentimes. The amount of money I spent on tools and materials is something I had lost count on, even though I tried to be as cheapskate as possible. Not to mention the amount of effort and time. I guess if the project is simple, and you have most of the tools, DIYing may be cheaper, I dunno. I wanted a custom digital clock that is not sold anywhere though, so I’d like to think my project was worth it for me.

That’s it. Finally I finished writing this long article which took 2 days to write. I have published all the code and design files, in case you want to know any technical details, or use them as references. 🐁

  1. A microcontroller is a micro electronic component that can be programmed to control circuits. It’s a very simple kind of computer.
  2. An Arduino Nano is basically the same as the most popular Arduino Uno, but smaller and cheaper. A Uno’s size is way too large in my opinion for something with this little processing power.
  3. A breadboard is a mainly-plastic board with holes all over them. One can insert electronics and wires onto it and have them work without soldering.
  4. PWM means turning on and off the LED so quickly that it appears to the human eye as if it’s continuously on but dimmer. Adjusting how much time it’s on verses it’s off changes how bright or dim it appears.
  5. A key switch is what’s underneath the key cap on a mechanical computer keyboard. Although it’s primarily used for keyboards, in terms of how it interacts with electronics, it’s the same as a regular button. I had the 4 key switches left over from repairing my computer keyboard.
  6. Buttons are mechanical construct, in the real world, the mechanical contacts will swing back and forth a bit when they are pushed together or released away. So pressing a button by hand once will be seen as the electrical contact has been closed and opened multiple times thus being thought as multiple key presses. Debouncing is the act of removing the effect of these unwanted openings and closings.
  7. A perf board is something like a circuit board, but with no internal connections and have a matrix holes filling the entire surface (So it doesn’t need to be custom-made). One would insert components on one side and solder and connect them on the other side. Search for images should give you a good idea on what it is.
  8. Epoxy resin is not the same as epoxy glue, although they are similar. Epoxy glue usually sets faster (tens of minutes), have a color tint, and sold in small volumes. Epoxy resin usually sets slower (about a day), are clear, and sold in larger bottles. Epoxy resin seems like a pretty popular material to make artistic objects, like custom key caps.

Unless otherwise stated, the original contents on this website are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.While reposting, please state that the article comes from FiveYellowMice's Blog, and include a link to the original article.

ホロ's avatar

Hyper-V 和 OpenWrt 搭建软路由


因为咱不想每一个虚拟机上就 …

HY's avatar


一转眼博客已经有1年多没写了。 刚好这个机会清空一下历史文章,重新开始。 也不知道这次能坚持多久_(:3」∠) […]
ホロ's avatar

咱和()的 2020 年?

就不要 不合时宜的 问为啥隔了接近三个月才写了…… (?)

以及第四年的总结 在咱的 Matters 上。



Google Search Console 的表现,是怎么样呢?


因为众所周知的原因(COVID-19 啦……),于是几乎都是呆在家里, 然后被批判一番不务正业…… 那就记一下这一年玩过的游戏好了。

  • DJMax Respect V ,除了正式发售时突然降价引起抢先体验玩家不满和全程联网外加笨蛋反作弊插件以外, 只是 DJMax 这个老字号就能让咱接受前面的大部分缺点了。
  • 女神异闻录5 皇家版,给女神异闻录系列出加强版是 ATLUS 老传统了。
  • 集合啦!动物森友会,成功登上咱 Switch 游戏时间排行第一。
  • Project DIVA MEGA39s,咱为什么要买它…… FTDX 加上 DLC 它不香么……
  • Fluffy Store 和 ATRI -My Dear Moments- ,两部被推荐来看后觉得不错的视觉小说。(所以 fault - SILENCE THE PEDANT 还是没有出)
  • Untitled Goose Game,其实 2019 年在 PS4 上玩过了,今年借着在 Steam 上发售又玩了一遍。 (然而并没有人一起玩更新的双人模式……)
  • 妄想破绽和原神……一言难尽*2。
  • Halo: The Master Chief Collection,用手柄坑完了除了 ODST 以外的正常难度剧情,嗯…… 至少咱的老笔记本还跑得动。
  • 女神异闻录5 乱战 魅影攻手,第一次录下了全程 然后给友人做素材工具人(?)
  • Cyberpunk 2077,除了分不清是 Bug 还是剧情演出以外还好啦。 这话到底是赞美还是批评……
  • Spice & Wolf VR2, “什麼?你說今天12/10還有2077? 2077能有赫蘿嗎?” 新的动画鉴赏模式虽然短了点……
  • Stellaris, P社玩家没有一个无辜的……

当然除了这些以外还有些一直在玩的老游戏,也就是那些音游什么的。PSN 给咱发年度报告的时候去年游玩时间最长的 游戏还是 FTDX ……


tcdw's avatar

新的桌面工作站:HP Z2 G5 Tower

我的上一代桌面工作站是台戴尔的 Inspiron 660,购于 2013 年初。经过数年来的多次升级,它拥有了三代 i7 处理器、16GB 内存和固态硬盘。

其实这台机器的性能直到今天依然不算逊色,基本上可以满足日常使用。但是,因为一些个人原因,我开始日常需要开启多个虚拟机,于是这台机器的 CPU 就开始有些吃不消了。

考虑到这台机器已经有 8 年历史,于是我便决定让它退居二线,同时将主力工作站进行升级换代。

经过反复的挑选,我最终选中了惠普的 Z2 G5 Tower。它在这些方面非常吸引我:

  • 免工具维护
  • 极高的可扩展性,有着合理的升级路线
  • 没有光污染,但是不失颜值的机箱箱体

在我购买这台机器时,我其实已经听说了 11 代酷睿系列处理器即将发布的消息,但是我这台机器预期服役 5 年以上,感觉差上一代应该问题不大。

以及我买的配置其实听起来就感觉有点尴尬:i7-10700 / 8GB DDR4 2666 x1 / 2TB 机械硬盘,不过我自己已经有一些配件了,可以直接换上去用。

最终,成功在国内商家以 6.8k 拿下。




  • 正面 IO:
    • 2 个 USB 3.1 Gen1
    • 2 个 USB 3.1 Gen2
    • 耳麦接口
  • 背面 IO:
    • 2 个 USB 2.0
    • 2 个 USB 3.1 Gen1
    • 2 个 USB 3.1 Gen2
    • 1 个 RJ-45(I219-LM,千兆以太网)
    • 线路输入和线路输出
    • 2 个全尺寸 DisplayPort
    • 1 个 VGA (Flex IO modules)





  • 4 条 DDR4 内存插槽
  • 4 个 SATA 3 接口
  • 两个 M.2 M Key 插槽(只支持 2280 尺寸)和一个 M.2 E Key 插槽(我这台已经预装了 AX201)
  • 4 个 PCI-E 插槽


传说中的 Flex IO modules;我这台配的是 VGA 输出。

预装的 AX201 网卡,支持 Wi-Fi 6 和蓝牙 5.0。

主板的 PCI-E 插槽,配置如下:

  • 1 个 PCI-E x16
  • 2 个 PCI-E x4(x1 信号)
  • 1 个 PCI-E x16(x4 信号)


我的附带了一个笔记本尺寸的 DVD-RW 驱动器,不过还预留了一个空的 5.25 寸扩展槽。

700W 的电源适配器,有着 80 Plus 铂金认证。预留了两条 6+2 的显卡供电。


Edit: 有 Telegram 群友指出这个电源适配器应该是符合 ATX12VO 标准的。电源参数看起来确实如此,但是实际上孔位和尺寸跟正常的 ATX 电源完全对不上,而且电源接口也是非标准的。

三星 8G DDR4 2666 内存。

东芝 DT01ACA200 机械硬盘(2TB / CMR);拆下来以后被我塞进 NAS 里了。

硬盘托架;这台机器可以安装两块 3.5 寸硬盘。






  • 显卡:ZOTAC GTX 1650 Super (4GB GDDR6)
  • NVMe SSD:铠侠 RC10 500GB
  • 内存:英睿达 16GB DDR4 2666



那么把整个散热器都拆掉呢?我试了,结果机器在 POST 就会报错,抱怨风扇出现了问题。




得益于 24G 的内存、8 核 16 线程的处理器和 Hyper-V,整体的体验是滑溜溜的,开 3 个 Windows 虚拟机都压力不大。

2021 年 4 月更新:因为发现 500G 的存储空间有点捉襟见肘,再加上 Chia 挖矿潮即将兴起,所以提前把固态硬盘换成了建兴 T10 240GB + 铠侠 RC10 1TB 的组合。很爽。


感觉这台机器买的很值,相比我 8 年前的旧电脑,是一次非常巨大的飞跃。开心!

顺便一提,我在购买这台机器时,有考虑上一块更好的显卡,但是现在显卡实在是贵的太离谱了,就先把之前买的 GTX 1650 Super 装上了。(摊手

ホロ's avatar

Arch Linux 上的 GLNMP


请允许咱引用一下 lilydjwg 的一段话:

我之所以现在记录这事儿,「现在」的原因是,我要在另一个系统上再测试一遍再发布出 …
RecursiveG's avatar

Ciphersuite Memo

I'm sorry if you landed in this keywords soup only to find it not helpful.

  • Key Exchange

    • DH (Diffie-Hellman): \(g^{xy} = (g^x)^y = (g^y)^x\)
    • ECDH (Elliptic-Curve DH)
    • ECDHE (ECDH Ephemeral)
    • DHE (DH Ephemeral)
    • RSA (Encryption): Generate a random bitstream and share it with the peer by encrypting using peer's RSA public key.

    A related concept is PFS (Perfect Forward Secrecy). DH offers PFS while RSA cannot.

  • Authentication
    Also known as key-signing. Commonly used together with the PKI(Public Key Infrastructure).

  • Encryption
    Used for data confidentiality.

    A related concept is mode of operation,which turns a block cipher to a stream cipher. CBC is a commonly used one. When it's used with AES, it's expressed as AES-CBC

  • Message authentication
    Used for data integrity. These algorithms are also called MAC(Message authentication code).

  • Authenticated Encryption (AE)
    Combines confidentiality and integrity. Wikipedia: Authenticated Encryption.

    • EtM (Encrypt-then-MAC): A secure way to combine encryption algorithms with MAC algorithms.
    • GCM (Galois/Counter Mode): A mode of operation, when paired with a block cipher, offers AE (actually AEAD) in one step.

    Some commonly used AE methods:

    • AES-CBC with an HMAC e.g.AES128-CBC-HMAC-SHA256.
    • ChaCha20 with Poly1305.
    • AES-GCM
  • Authenticated Encryption with Associated Data (AEAD)
    Similar to AE, but allows extra unencrypted data (associated data) to be authenticated. Roughly speaking:

    ciphertext = Encrypt(plaintext)auth_tag = Mac(associated_data + ciphertext)

    A common use case for AEAD is when encrypting a network packet, you want the packet header to stay unencrypted (for network routing purposes) but still authenticated.

  • Note about DH and curves of EC-based algorithms
    DH-based algorithms may have a “Group” option, which specifies a prime field or an elliptic curve. If a prime field is used, such as modp2048, it's normal DH. If an elliptic curve group is used, such as ecp256, it's EC-based DH.
    Some other curves may be used:

AstroProfundis's avatar

用 openSUSE Kubic 搭建一个 Kubernetes 集群

最近给家里几台闲置机器拆开重新整理了一下,然后统统装上了 Proxmox 做虚拟化,然后开出小鸡来想着搞点没玩 ...>>Read more
OX's avatar

做了一個Amazon Echo Dot (3rd Gen.)的固定支架

根據自己的需要製作的模型,用來固定Amazon Echo Dot第三代在牆上或者任何平面上,甚至天花板。

沒有考慮電源適配器的擺放,單純只是固定Echo Dot,一般來説,使用這個支架需要一個穩定的釘子或者螺絲。








下載模型檔案(STL):echo dot 3_1.5-stl



這篇文章 做了一個Amazon Echo Dot (3rd Gen.)的固定支架 最早出現於 OXの胡說八道

's avatar



Phoenix Nemo's avatar

在 Linux 系统中升级超微 BIOS 固件

最近突然有客户找来说 BIOS 进不去了呀…看截图 stuck 在 POST Code AB 大概就知道什么情况了。

这不就是经典的 y2k bug 再现嘛…

一般情况下,升级超微 BIOS 固件的推荐方式是制作 DOS 启动盘引导系统升级,不过现在 BIOS 进不去也没有物理 access 就只好在 Linux 里操作啦。


简单命令 lshw

~> lshw | head -n 25
description: System
product: X9SCL/X9SCM (To be filled by O.E.M.)
vendor: Supermicro
version: 0123456789
serial: 0123456789
width: 64 bits
capabilities: smbios-2.7 dmi-2.7 smp vsyscall32
configuration: boot=normal chassis=desktop family=To be filled by O.E.M. sku=To be filled by O.E.M. uuid=[redacted]
description: Motherboard
product: X9SCL/X9SCM
vendor: Supermicro
physical id: 0
version: 1.11A
serial: [redacted]
slot: To be filled by O.E.M.
description: BIOS
vendor: American Megatrends Inc.
physical id: 0
version: 1.1a
date: 09/28/2011
size: 64KiB
capacity: 8128KiB

X9 主板不是 2015 年就 EOL 了吗这可真是够老了…

总之主板型号是 X9SCM 于是在超微找到了新版的 BIOS 固件。下载到系统中解压得到一堆文件,其中 X9SCM1.106 这个文件就是需要的 BIOS 固件本体。

编译 SUM 内核模块

Supermicro Update Manager (SUM) 是用于在系统中控制 BIOS/BMC 的程序。首先下载并解压,得到 sum 二进制文件和一堆其他东西。在 driver 目录中发现了对应发行版预编译的内核模块,但是直接 insmod sum_bios.ko 出错,好在它也提供了源码,那么就直接编译吧。

首先安装对应内核的 Linux 头文件,搜索 linux-headers 一般都可以找到。

~> uname -a
Linux localhost 4.19.0-13-amd64 #1 SMP Debian 4.19.160-2 (2020-11-28) x86_64 GNU/Linux


~> apt install linux-headers-4.19.0-13-amd64 build-essential # 还需要 make 和 gcc

进入 driver/Source/Linux 执行 make,如果成功编译,则 insmod ./sum_bios.ko



升级命令和其他主板商以及 RH 系的包命名风格一样喜欢大小写混合 = =…

~> ./sum -c UpdateBios --file ../X9SCM1.106/X9SCM1.106
Supermicro Update Manager (for UEFI BIOS) 2.5.1 (2020/11/12) (x86_64)
Copyright(C) 2013-2020 Super Micro Computer, Inc. All rights reserved.

WARNING: BIOS setting will be reset without option --preserve_setting
Reading BIOS flash ..................... (100%)
Writing BIOS flash ..................... (100%)
Verifying BIOS flash ................... (100%)
Checking ME Firmware ...
Putting ME data to BIOS ................ (100%)
Writing ME region in BIOS flash ...
- Update success for /FDT!!
- Updated Recovery Loader to OPRx
- Updated FPT, MFSB, FTPR and MFS
- ME Entire Image done
WARNING:Must power cycle or restart the system for the changes to take effect!



以及新版 X10 开始升级 BIOS 需要激活 license 了… 嘛。听说超微的密钥早就被提取出来写了算号器了(什

最后的最后,提醒各位到 2038 年只有 17 年了哦(笑

's avatar

Sepia Memories -- Let's Talk about Sepia Tears, and some Free Games

Back to 2016

I was an undergraduate then, and I had an Honor 7. One of the memories I had with that phone is searching everyday for a Marshmallow update. Don’t get me wrong, I still think it good to have an Android phone for the freedom it provides, although I make do with Apple for now.

And at that time I was hunting for some free games in the play store. Android phones usually don’t have access to Google services in Mainland China, so most cases I would check out some third-party app markets for applications or games. Except when I expected something different.

RecursiveG's avatar

IPsec 配置备忘 Part9 - RouterOS

这篇来填一个在 Part1 的时候挖的坑,简单介绍一下怎么在 RouterOS 和 strongSwan 之间配置一个 Site-to-Site 的 IKEv2 VPN。如果你还没有动手实际配置过 strongSwan,我强烈建议你先读完至少 Part1~5 和 Part7,并且自己动手配置一个能用的 strongSwan 服务端。这样至少能保证你会配置 strongSwan,否则同时学习 RouterOS 和 strongSwan 两种配置方法会让人云里雾里。


RouterOS 做路由器,用 DHCP 给内网设备分配192.168.50.0/24段中的 IP 地址。并且用 IPsec 的 Tunnel 模式将所有来自此 IP 段的数据转发至服务器22.22.22.22。路由器本身的 IP 并不重要。

阅读基本 RouterOS 命令

在本文中我主要使用 RouterOS 命令来表示具体的配置,但是实际情况下调试 WebUI 会更方便。这里提供一张简图解释怎么阅读 RouterOS 的命令:

RecursiveG's avatar

Flexget+systemd.timer 配置

之前一直都是手动检查每周新番并添加到 Transmission 中的,最近尝试把这一流程自动化一下。简单搜索了一下发现了 FlexGet 这个工具。支持 Transmission 也能在 Linux 下运行,就决定是它了。


ArchLinux 源里并没有这个包,又考虑到这是个 Python 程序,所以就决定直接用venv了:

mkdir ~/.config/flexget
cd ~/.config/flexget
python -m venv virtualenv
source virtualenv/bin/activate
pip install flexget
pip install transmission-rpc



host: ''
port: 443
username: 'your-username'
password: 'your-password'

rss: ''
accept_all: yes
template: transmission
path: '/folder/path/on/server'

Systemd 配置

Description=Refresh RSS with flexget

ExecStart=/home/recursiveg/.config/flexget/virtualenv/bin/flexget execute

用户登录后 5 分钟进行同步,然后每两小时检查一次。

Description=Run flexget on login then every 2hrs




# 启用并运行定时器
systemctl --user enable --now flexget.timer
# 立即检查
systemctl --user start flexget.service
# 检查定时器情况
systemctl --user list-timers
# 检查日志
journalctl --user-unit flexget.service -f
tcdw's avatar

我的 2020

好耶,我终于把 2020 年勉强过完了。


我在 2019 年终总结里写道:


结果,受到 COVID-19 疫情的影响,今年的大计划其实几乎都泡汤了(包括计划参加的一个比赛、2020 年夏天的面基出游计划等)。




  • WI-1000XM2
    听感很好,佩戴起来相当舒适,而且 10 个小时的续航还是很香的。
  • 自组装台式机
  • 一加 8T
    强大的骁龙 865、65W 快充(虽然是私有协议)、滑溜溜的 120Hz 屏幕、很强的可折腾度……用起来太爽了!
  • AirPods Pro




  • 将评论系统后端用 TypeScript 进行了重构,修正了很多错误
  • 给自己博客增加了暗色模式,并支持跟随系统切换
  • 给自己写了一套全新的卡片式博客主题,使用了新的设计语言

2021 年计划?

SgDylan's avatar

同 2020 道别、相约 2021

再次,同样很普通地,为 2020 划上句号吧。

imi415's avatar


RecursiveG's avatar

在 GNS3 中使用 ArchLinux

之前在折腾 strongSwan 配置的时候需要多台电脑互相连接做测试,用实机各种不方便。比方说,我不想暴露真实 IP,就只能在发布前手工编辑配置文件,也不知道编辑过后的文件到底能不能工作。于是尝试折腾使用 GNS3 来搭建一个虚拟的网络环境。

安装 GNS3

你可以尝试从 AUR 安装 GNS3,但是我之前试了几次都不是太成功,于是还是把 GNS3 装进了 virtualenv 里:

# 创建并进入 virtualenv
virtualenv -p python3.9 virtualenv
source virtualenv/bin/activate
# 安装 GNS3 及 PyQt5
pip install gns3-server gns3-gui pyqt5
# 安装一些其他的依赖(可能有漏的,请根据 gns3 的出错消息自己安装)
yay -S vpcs dynamips
# 启动 GNS3

GNS3 支持好几种方式来运行虚拟网络中的“节点”,我这里使用 Docker 来运行虚拟的 Archlinux 系统。因此也需要先安装:

sudo pacman -S docker
sudo usermod -aG docker `whoami`
sudo systemctl start docker

同时建议安装 Wireshark 方便抓包调试。


进入 GNS3 创建 Project 以后就可以尝试把左侧列表里的设备往中间画布上拖了。但是你会发现不仅设备类型少的可怜而且也基本拖不上去。这时我们需要自己创建设备节点的模板。进 Preferences,在最下面的 Docker containers 里点 New,Image name 输archlinux:base-devel,其他的都默认,Adapters 可以按自己需要指定数量。

点 OK 后,左侧的设备列表里就多了一个设备。拖到画布上,右键 Start,再右键 Console 就可以看到 ArchLinux 的命令行界面了。

GNS3 默认使用 xterm 作为终端模拟器,如果你像我一样使用 Gnome Terminal,需要先去 General - Console applications 把启动终端的命令改成

gnome-terminal -t "%d" -- telnet "%h" "%p"

这个新创建的 Archlinux 节点还是空空如也的,除了基本的系统什么也没有,我们可以把它连接到实际的网络上,这样就能用 pacman 安装软件包了。先从设备列表里拖一个“Cloud”设备出来,然后点左侧边栏最下面的“Add a link”切换到连线模式,点 Cloud 节点,选择物理机上用于联网的接口,再点 Archlinux 节点,选择要连接的端口,这样一条连线就接好了。Archlinux 节点被直接桥接到了外部接口上,和宿主机位于同一网段。

由于 Archlinux 的镜像不带 dhcpcd 无法自动获取 IP,只能手动设置一下咯:

ip link set eth0 upip addr add dev eth0ip route add default via dev eth0echo nameserver > /etc/resolv.conf

不过实际测试以后发现这个速度实在是很残念,可能还是要写 Dockerfile 把镜像配置好再用才行。

violet's avatar



去年因为这样那样的原因以及一些骚操作加自尊心,本来有个亚麻 offer 也浪费了,当时的想法是老子来了之后再找,我要是知道后来发生啥非得抱着人大腿把 offer 求回来。


这里要非常感谢我在前东家的老板,帮我办了几个月的远程办公,一定程度缓解了我找工作的压力。虽然远程工作没有很重的业务需求,不过也非常锻炼人的沟通能力,我以前觉得沟通对于我来说小菜,后来发现如果涉及到语言障碍和思维方式其实还是门深的学问。当然不要来跟我杠我的英语能力,我在口语上没有啥问题,虽然跟 native speaker 拼不过,但是不算差的那派。主要还是跟人沟通的思维方式上,如何进行有效的沟通也是一门学问。


老实说我不会刷题,上大学的时候因为 C++ 的作业是 ACM 题,我真是哭了又哭,反正我也不是啥好学生,题是真刷不动。但是为了找工作不得不开始刷,Leetcode 买了会员,分门别类开始学习。我其实更想说明区分刷题和学习,刷题只是过了一遍题,学习是学习怎样写这样的题,怎样的方式可以优化,遇到题想到考点在哪里。随着深入学习还要脱离编辑器的辅助,努力追求正确率,都是需要练习的事。人老说做题家刷题家,我无所谓,反正是面试敲门砖而已,我做好自己就行,自己躺地上嫌别人学习那我没得办法。


我一直怀疑2020是我的本命年,甚至我追问了很久我妈是不是生错时间了。由于众所周知的原因,3月开始 lockdown,当时又面了一次亚麻,但是因为 virtual 面试写题的时候有点慌乱,没有 bug free 所以就挂了。之后很长一段时间网上也鲜有新职位,我就老实刷了一段时间题。6月开始不断投简历面试,怎么说……就是觉得差点缘分,有面着面着没信的,有面试完全没问题我还教面试官怎么写回头就跟我说实在抱歉,有 OA 做完明明 90% 以上但还是跟我 unfortunately 的,有等着安排面试时间回头跟我说他们找到人了,有以我没有 PR 拒的,更不要说简历投出去石沉大海的。期间也有几个猎头在紧密联系,不过都是些 start up,一个脾气不对就挂了(我还没说他们给我乱安排面试时间还放我鸽子),总之就是一刷邮箱眼泪就掉下来。我从来没有经历过如此低谷的时刻,每天都在自我反省到底我哪里做错了觉得我也没有这么差劲吧,脾气暴躁跟大柱一言不合就摔电脑,一刷 SNS 看大家的工作生活如此丰富多彩就开始难过。还有些人落井下石给我添堵我谢谢你啊。

很长一段时间都不想上网,啥都不想干,完全靠着大柱和几个朋友的支持坚持过来。大柱在我哭我撒泼我沮丧我难过的时候乖乖地抱着我。悦姐看我不说话不理人找我视频聊天,wandou 也在鼓励我,大毛还跟我一起动森聊了会天还给我寄了口罩,现在想起来都是我想流着眼泪感谢的人。

好在投了好久简历之后终于接到现在这家的 offer,在面试的时候就让人觉得很舒服,面试官(我后来知道是个士大夫)水平真是高,好多东西我觉得没问题,经过他一点播才发现噢!有道理,而且还有几个面试官聊过之后觉得真不错,有了 offer 就马马接了。事实证明这家真的不错,虽然公司小,但是同事水平是真高,我以前不觉得自己拉胯,现在发现最拉胯的是我(我要赶上!)




via these people and places