TimothyQiu's Blog

keep it simple stupid

Arch Linux 下 Display Manager 黑屏

最近 MBP 电池彻底坏了不想修,换 Windows 用了几天又很不习惯,于是就装了个 Arch。

用 Arch Linux 得自行装图形界面、使用 Display Manager 进行图形化的登录操作。但这时候就遇到个很好玩的事情:进 Display Manager 时黑屏(如果去 BIOS 里绕一下,有小概率不黑屏),不过在黑屏里 Ctrl+Alt+F2 还是可以切到别的 tty 的。

内核参数、驱动之类的检查一圈都没有问题,把我用的 LightDM 换成别的 Display Manager 也还是如此。最后在万能的 Arch Wiki 找到了解决方法,在 /etc/lightdm/lightdm.conf 里配置一下:

[LightDM]
logind-check-graphical=true

黑屏的原因是,图形驱动还没加载完,LightDM 就启动了:系统启动得太快了,得显式让 LightDM 等待图形设备 🤣

凤凰点阵体

做像素风/复古游戏不免要用上点阵字体,之前我用过一阵子 IPix,但它时有缺字,一些英文、数字、以及标点符号的样子和字间距给人感觉也有点奇怪。于是我就自己做了一个像素字体。

做英文字体和中文字体所需要涉及的字形不在一个数量级上,前者撑死了一百个左右,后者则至少要覆盖常用的几千个汉字,非常体力活。IPix 的字模来自于开源项目 BitmapFont,这个项目的作者收集了很多远古系统里的字模数据(感觉从老系统里提取的字模在版权方面界限还是有点模糊),可以直接生成对应的字形。

这些字模数据中,每一个字符都对应一系列的比特位。比如 16 像素宋体的「人」字在数据文件中对应 01000100010001000100010001000280028002800440044008201010200e4004,按照每 16 个比特位换行表示后就成了点阵的「人」字:

       X
       X
       X
       X
       X
       X
       X
      X X
      X X
      X X
     X   X
     X   X
    X     X
   X       X
  X         XXX
 X           X

有了这样的点阵数据,我们就可以很繁琐方便地制作各种格式的字体文件了。

我把制作好的 TrueType 字体放到了 itch.io 上,有需要的童鞋可以自取:

p.s. 其实市面上有一款非常不错的收费点阵字体 丁卯点阵体,支持 7px 和 9px 的大小。

花10分钟用Godot开发平台跳跃游戏

10 分钟的内容,录了两天。一边解说一边写代码,然后还得控制在 10 分钟左右真是太难了,深刻体会到了舌头打结的快感。

BV1Xi4y1M7Eq

p.s. 上一次 B 站投稿居然已经是在八年前了。

夹带私货?

比如前段时间某游戏中出现了不合适的内容,很多人把这件事描述为「制作团队夹带私货」。语文老师看了肯定会很生气:

只要把「私货」换成中立的「自己的观点」、把「夹带」换成中立的「加入」,就能很方便看出语言逻辑上的谬误:

改换以后,你甚至可以发现自己反对的并不是「加入自己的观点」的行为,只是单纯地反感这个观点本身。

情绪化的「夹带私货」并不是在尝试描述问题,而是在逞一时的口舌之快,带来的只能是一滩浑水。无论是「私货」「洗地」「卖惨」还是「带节奏」「泼脏水」「三观不正」「不是笨就是坏」,真的不得不佩服国人对于「骂人不带脏话」的莫名追求。

PostgreSQL 窗口函数

原来除了 Modern C++、Modern CMake,我们还有 Modern SQL,真是佩服这种文艺复兴式的 branding。

窗口函数(Window Function)就是一个例子,它由 SQL:2003 引入,可以用来筛选结果集中与当前行存在指定关联的行。相比子查询,效率更高,用起来也更方便。

例如我们有一张去年全年每日收入的表 revenues,想根据这张表查一张报表,显示每季度总收入及其环比增长,就可以用窗口函数:

created_at revenue
2019-01-01 123.45
2019-01-02 456.78
2019-01-03 420.00
... ...
SELECT
    date_part('quarter', created_at) AS quarter,
    sum(revenue) AS revenue,
    (sum(revenue) /
        lag(sum(revenue)) OVER ()) - 1 AS percentage
FROM revenues
GROUP BY 1
ORDER BY 1

得到的结果类似这样:

quarter revenue percentage
1 4530.50
2 4565.64 0.008
3 4933.01 0.080
4 4731.75 -0.041

SQL 中的 lag(sum(revenue)) OVER () 就是对窗口函数的调用了,其中 lag 函数就表示「上一条记录(季度)」。

窗口函数调用的特征是关键词 OVER

窗口的定义

「窗口定义」中的窗口其实英文叫 Frame,即窗框。「窗口函数」中的窗口则是 Window,即窗户。没什么特别含义,应该就是叫着顺口、想着形象而已。

下面的例子里,我们用 PostgreSQL 的聚合函数 array_agg 列出窗口中有哪几行。

所有记录

括号中留空表示窗口中为结果中的所有行:

SELECT
    i,
    array_agg(i) OVER ()
FROM generate_series(0, 5) AS s(i)
ORDER BY 1
i array_agg
0 0,1,2,3,4,5
1 0,1,2,3,4,5
2 0,1,2,3,4,5
3 0,1,2,3,4,5
4 0,1,2,3,4,5
5 0,1,2,3,4,5

我们可以看到每一行的对应窗口里,都包含了所有其它行。

相同分组

括号中还可以使用 PARTITION BY 指定分组的条件:

SELECT
    i,
    array_agg(i) OVER (
        PARTITION BY i % 2
    )
FROM generate_series(0, 5) AS s(i)
ORDER BY 1;
i array_agg
0 0,2,4
1 1,3,5
2 0,2,4
3 1,3,5
4 0,2,4
5 1,3,5

我们可以看到每一行的对应窗口里,都包含了与它 i % 2 值相同的行。

指定范围

可以用 ROWS BETWEEN A AND B 来指定窗口中包含哪些行,例如:

SELECT
    i,
    array_agg(i) OVER (
        ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING 
    )
FROM generate_series(0, 5) AS s(i)
ORDER BY 1;
i array_agg
0 0,1,2,3,4,5
1 1,2,3,4,5
2 2,3,4,5
3 3,4,5
4 4,5
5 5

这里其实直接读 SQL 就明白了,是要求窗口从当前行开始,一直到最后一条记录结束。

排序

窗口定义里还可以用 ORDER BY 来排序,不过一旦排序,默认的范围就变成了 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRERNT ROW(从开头到当前行),如果不是想要的范围就需要显式指定。

SELECT
    i,
    array_agg(i) OVER (
        ORDER BY i DESC
    )
FROM generate_series(0, 5) AS s(i)
ORDER BY 1;
i array_agg
0 5,4,3,2,1,0
1 5,4,3,2,1
2 5,4,3,2
3 5,4,3
4 5,4
5 5

常见窗口函数

参考