使用 Rust 编写跨平台图形界面程序。
Tauri 是一个跨平台桌面应用框架,使用 Rust 作为后端,前端基于 Web 技术(HTML/CSS/JS)。
相比 Electron,它更轻量、安全,通过 Rust 提供系统级功能,并支持 WebAssembly 优化性能。
适用于构建高效、小巧的桌面应用,兼容 Windows、macOS 和 Linux。
在 Linux 上可以使用 npm create tauri-app@latest
创建新的工程,使用 npm 进行项目管理,
那么是否可以在 Linux 进行交叉编译呢?官方网页上有交叉编译的介绍,目前还是实验性质,
我尝试了一下,将遇到的问题记录在这里。
首先要安装 rust 的交叉编译工具,并使用 cargo-xwin
作为编译工具,它会自动下载 Windows
环境的 SDK 和工具链:
$ rustup target add x86_64-pc-windows-msvc
$ cargo install --locked cargo-xwin
Linux 平台应该也需要一些依赖,官网只提到了 llvm
,lld
,由于我的电脑上工具比较全,有
机会用 Docker 查看一下完整的依赖。
还要安装 NSIS 作为安装包构建工具,这个比较麻烦,官网的做法在我电脑上行不通,我主要采用
源码编译的方式:
$ wget https://prdownloads.sourceforge.net/nsis/nsis-3.08-src.tar.bz2
$ tar xfv nsis-3.08-sre.tar.bz2
$ cd nsis-3.08
$ scons makensis SKIPPLUGINS= all SKIPUTILS= all
$ sudo cp build/urelease/makensis/makensis /usr/local/bin
这里只编译了 makensis
这个二进制文件,其它的内容从 tauri 的官网提取最可靠。
$ wget https://github.com/tauri-apps/binary-releases/releases/download/nsis-3/nsis-3.zip
$ unzip nsis-3.zip
$ sudo cp -a Include Contrib Include Stubs /usr/local/share/nsis/
经过这样配置就可以用下面的命令生成 Windows 平台的目标文件了:
$ npm run tauri build -- --runner cargo-xwin --target x86_64-pc-windows-msvc
写入 Openwrt 固件的时候一不小心弄错了分区,结果砖了。
手头的小米 AX3000T 路由器官方固件是 1.0.64 版本,国内好多论坛上都说要降级到 1.0.47 版本才可以
获取 ssh 权限,进而写入 OpenWRT 固件。但阅读了官网说明
发现现在不用降级了,直接运行一个脚本即可。接下来按照官方文档,备份了原始固件,使用 nc 将固件
复制到主机,主机运行:$ nc -lp 1234 | tar xvf -
,路由器运行:# tar cf - *.bin | nc 192.168.31.xx 1234
。
路由器的 nc 好像不支持 -l
参数,因此 OpenWRT 的固件通过 tftp 传递过去,然后即可用 ubiformat
写入到闪存中。
问题就出在这里,写入闪存的时候要根据内核参数 firmwware=?
来决定写入哪个分区,并设置相应的
启动参数。结果我把这个弄反了,重启之后路由器在网络上没有任何响应,各种按 reset 的组合都试了
一遍,也没有任何有效的结果。
为了能获得状态信息,只好打开路由器后盖(很紧,费了很大力气才打开),接上 UART 转接线。从串口信息
可以看到启动到 BL3 就停住了,无法执行后续的程序。同样根据官方文档指出的一个论坛文章 ,
使用一个 mtk_uartboot
工具,可以通过串口下载执行指定
的固件。除了论坛中指出的固件下载位置,其它固件可以在 OpenWRT 的固件快照页面 下载。具体使用的命令为:
$ mtk_uartboot -s /dev/ttyUSB0 --aarch64 -p bl2-mt7981-bga-ddr3-ram.bin -f openwrt-mediatek-filogic-xiaomi_mi-router-ax3000t-ubootmod-bl31-uboot.fip
执行完命令后用 minicom 查看路由器的串口消息,发现固件一直尝试从 tftp 下载后续固件,将相应固件
放到 tftp 目录后即可正常启动。
此时如果重启的话路由器依然无法启动,必须重新使用 ubiformat
命令将固件写入正确的分区。由于此时
没有 tftp 命令,最后通过 wget 将新固件下载并写入。
此时的文件系统里也没有 nvram 命令,不知道写错的启动参数是否还需要修改。
准备使用 ventoy 制作一个多功能启动盘。
“Ventoy 是一个制作可启动 U 盘的开源工具”,项目主页上如是说。下载 GUI 版本的安装工具,
直接运行安装即可。安装过程会重新格式化我的 U 盘,我在安装前设置了空余一部分硬盘空间自用。
安装完成后,第一个分区是 fat32 分区,可以作为普通 U 盘使用,也可以放入 ISO 等映像文件,
Ventoy 启动的时候会自动找到可启动映像加入菜单,选择即可运行映像中的操作系统。支持大部分
的操作系统,一般使用非常方便。
之前空余的空间自然是用来安装一个正常的 void 发行版。我分了两个分区:一个 ext2 格式用来
做 boot 分区,一个 btrfs 用来一般使用。btrfs 分区设置了两个子卷,@void 用来作为根目录,
@home 用来作为个人目录。
直接使用下面命令安装基本系统:
# xbps-install -S -r /mnt -R "$REPO" base-system
其中 /mnt
是我挂载的根分区,$REPO
是我使用的镜像地址。安装完成后将我本地的 /etc/rc.conf
和 /etc/default/libc-locales
文件复制过去,手写一个 /etc/fstab
文件。使用 xchroot
命令
进入新文件系统,设置 root 的密码,使用 xbps-reconfigure -f glibc-locales
生成 locale 信息。
安装 grub 比较特殊,因为我不能安装到正常的 EFI 分区,那样会影响到 Ventoy 的启动,因此挂载了
boot 分区,将原来 boot 目录中的内容移动过去。建立 /boot/efi/
目录,然后运行下面的命令安装:
# xbps-install -S grub-x86_64-efi
# grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --no-nvram --force
使用 --force
参数是因为我的 efi 目录根本不是 EFI 分区,安装到这里只是为了将来使用 Ventoy
进行 chainloading。
最后执行 xbps-reconfigure -fa
确定所有的软件包配置成功。
在 U 盘的第一个分区设置一个 ventoy 目录,里面建立一个 ventoy_grub.cfg
文件,内容如下:
set mygrub=/grub/x86_64-efi/grub.efi
menuentry "Enter the Void" {
search --set=root --file $mygrub
chainloader $mygrub
}
这样在 Ventoy 运行时按 F6 就可以看到我的菜单,并启动 U 盘上的系统了。
准备把我的云主机上开放给其他用户,为了限制这些用户的权限,必须选择一个可以
控制权限的 shell。
选择 firejail 主要还是经过对比,感觉它功能丰富,配置也比较简单。它利用 Linux
的命名空间、Seccomp BPF 等技术,隔离应用程序的文件系统、网络和进程。通过配置
文件支持为每个应用程序定制配置文件,定义允许访问的资源。
本来应该可以直接把 firejail 作为用户的登陆 shell,但在应用后,用户登陆时显示
没有指定 shell,连接立即被断开。可是使用 login.user
指定 --shell
参数会提示
这个参数已经 deprecated。用 default.profile
指定 shell 参数也总是说格式不正确。
简单的解决办法是写一个脚本作为用户的缺省 shell,而这个脚本直接使用 firejail 来执行
bash。
#!/bin/bash
exec firejail /bin/bash
这样用户登陆的时候就执行了一个受限的 bash,限制的条件完全通过配置文件来决定。
但这样设置的结果是用户在远程无法通过 scp 或者 rsync 命令传输文件。开始还以为是某些
权限设置的问题,调整了很久都没有效果。深入了解了这些命令才发现,它们实际上是通过
缺省 shell 启动了远程的其它程序。而之前的脚本无法处理这种情况。
比如用户执行了 scp 之后,缺省 shell 实际会收到如下的命令行参数:
-c /usr/lib/openssh/sftp-server
我写的脚本必须要处理这种情况。因此修改起来也比较简单,最后的脚本如下:
#!/bin/bash
exec firejail /bin/bash " $@"
这样带来的问题就是用户登陆后只能使用 bash。想使用其它的 shell,比如 zsh 的话就只能
自己登陆后运行或写到 ~/.bashrc
中。firejail 的主要应用场景是在沙箱中运行不可信的
程序。所以缺省配置中对各种本地的配置文件都做了保护。比如 .zshrc
是无法修改的,这
对于使用 zsh 非常不便。相应的配置在 disable-common.inc
中,而这个文件在 default.profile
中被包含。简单的解决办法是取消这个包含文件,这样大部分的软件都可以正常使用了。
限制一个程序也非常简单。比如要让用户无法使用 ssh,就只需要在配置文件中增加:
一直不是 LSP(Language Server Protocol)的粉丝,总觉得配置比较麻烦,但最近
试用了 kickstart.nvim,LSP 开箱可用,感觉还是有一些帮助的。
kickstart.nvim 是一个单文件的 Neovim 配置文件,包含大量注释和最常用的插件配置。
最方便的在于可以自动安装缺失的插件。只要网络环境允许,安装了它的配置文件之后,
只要启动 nvim 就可以自动完成后续的配置。项目地址:kickstart.nvim 。
在安装了 kickstart.nvim 之后,立刻做了一些自己习惯键位的设置,并调整了部分插件,
的确感觉比较省心。我做的修改可以在我的 github 中找到:my nvim config 。
插件系统使用 Lazy.nvim,需要更新插件的时候可以执行 :Lazy
,根据菜单可以选择更新(Update)、
清理(Clean)等等。LSP 使用 Mason.nvim,也可以执行 :Mason
看到类似的菜单。
一个对新手很有帮助的插件是 which-key.nvim,可以通过浮动窗口提示可用的命令按键。
比如在命令状态下按下 g 键,它就会提示你后续可以按的按键,下面代码是部分提示内容:
d -> LSP: [G]to [D]efinition
D -> LSP: [G]to [D]eclaration
I -> LSP: [G]to [I]mplementation
LSP 的功能也比较强大,比如可以在整个工程内进行变量改名:<leader>rn
,还有一个是 Code Action,
通过 <leader>ca
启动,具体能实现的功能由 LSP 本身来决定。
不同 LSP 的配置还不同。比如在使用 lua_ls 的时候,用 Code Action 功能关闭了对某个建议的提示,
后续找了半天没有找到从哪里恢复设置。结果最后发现保存在了 ~/.config/.luarc.json
这个文件中。
而 pylsp 的设置需要在 nvim 的配置文件中进行修改。具体其它一些用法还要在将来逐渐领会。
让终端可以显示的更美观。
只从更换到 alacritty 作为主要的终端模拟器之后,一直对它很满意,唯一遇到的问题是部分字形无法
显示,例如 💻 是一个电脑的图标,但在我之前的终端上显示不出来。但这种字形平时很少遇到,网页
上显示也都正常,就一直没有管它。究其根源就是这些字形在我目前使用的字体文件中没有定义,所以
无法显示。
最近又试用了 ghostty,它却可以正常显示这些字形,于是萌生了提升 alacritty 体验的想法。经过调研
发现 alacritty 没有字体 fallback 的设置,只能依赖系统的设置。解决的办法就是创建一个字体配置文件:
.config/fontconfig/fonts.conf
,内容如下:
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<alias>
<family> JetBrains Mono</family>
<prefer>
<family> JetBrains Mono</family>
<family> Noto Color Emoji</family>
</prefer>
</alias>
<alias>
<family> JetBrains Mono Bold</family>
<prefer>
<family> JetBrains Mono Bold</family>
<family> Noto Color Emoji</family>
</prefer>
</alias>
<alias>
<family> JetBrains Mono Italic</family>
<prefer>
<family> JetBrains Mono Italic</family>
<family> Noto Color Emoji</family>
</prefer>
</alias>
</fontconfig>
这里定义了我如果使用 JetBrains Mono
字体的时候,如果遇到不能显示的字形,就要使用
Noto Color Emoji
字体来显示。设置之后重启 alacritty 就可以正确显示之前缺失的字形了。
Noto Color Emoji
是我为了这次调整新安装的字体,不知道有什么办法可以知道 ghostty
使用的什么字体,ghostty 甚至不依赖系统的字体设置。
alacritty 对字体的 ligature 没有支持,ghostty 支持的就很好。
云平台上并没有 void 的镜像,本文记录如何在一个新安装的 arch 云主机上,安装并配置 void。
在 arch 主机上使用的是 btrfs 文件系统,这会大大简化分区操作。首先看一下当前配置:
pluto@arch4 ~ > sudo btrfs su l /
ID 256 gen 126 top level 5 path var/lib/portables
ID 257 gen 126 top level 5 path var/lib/machines
ID 258 gen 61 top level 5 path swap
前面两个应该是 systemd 建立,目前都是空的,swap 是虚拟内存空间,需要保留。
由于还准备保留 arch,双系统共用 home 会比较方便,所以建立一个 home 子卷。
pluto@arch4 ~ > sudo btrfs su cr /@home
Create subvolume '//@home'
pluto@arch4 ~ > sudo cp /home/* /@home/ -a
然后设置 /etc/fstab
挂载 home
分区,添加如下内容:
UUID= <your-btrfs-device-uuid> /home btrfs subvol= @home,defaults 0 0
注意如果 /home
目录的权限是 700 的话,普通用户无法访问个人目录,需要改成 755。
重新生成启动配置文件,检查生成的文件是否合理:
pluto@arch4 / > sudo grub-mkconfig -o /boot/grub/grub.cfg
接下来把 arch 的 文件系统做个快照,以后作为 arch 的根目录,同时建立一个 void 的根目录。
pluto@arch4 / > sudo btrfs su sn / /@archroot
Create snapshot of '/' in '//@archroot'
pluto@arch4 / > sudo rmdir @archroot/@home
pluto@arch4 / > sudo btrfs su cr /@voidroot
Create subvolume '//@voidroot'
重启后确认文件系统挂载没有问题,删除原来根文件系统中的内容:
pluto@arch4 / > sudo mount /dev/sda3 /mnt
pluto@arch4 ~ > sudo find /mnt -mindepth 1 -maxdepth 1 ! -name '@archroot' ! -name '@home' ! -name ' @voidroot ! -name swap -exec rm -rf {} +
此时必须重新安装一下 grub
,否则可能进入到 grub rescue 模式(刚刚犯了这个错误)
pluto@arch4 ~ > sudo grub-install --efi-directory= /efi
重启进入 arch 之后,挂载 @voidroot
目录:
pluto@arch4 ~ > sudo mount /dev/sda3 /mnt -o subvol= @voidroot
按照官方安装文档 安装 void。
需要注意的主要有以下几项:
允许 dhcpd 和 sshd 两个服务,否则重启之后可能无法连上主机。
bash-5.2# ln -s /etc/sv/dhcpcd /etc/runit/runsvdir/default/
bash-5.2# ln -s /etc/sv/sshd /etc/runit/runsvdir/default/
不重复安装 grub,使用 arch 的启动管理器,在 /etc/grub.d/40_custom
中
填入如下内容。
menuentry 'Void Linux' {
set root= 'hd0,gpt3'
linux /@voidroot/boot/vmlinuz-6.6.59_1 root= UUID= <your-btrfs-device-uuid> rw rootflags= subvol= @voidroot loglevel= 3 quiet console= tty0 console= ttyS0,115200
initrd /@voidroot/boot/initramfs-6.6.59_1.img
}
由于共享 home 分区,添加用户,注意 uid 和 gid 是否相同。
注意用户的 sudoer 设置,使用户可以获得 root 权限。
双系统使用不同的 ssh key 会让客户端很麻烦,因此将 arch 系统中的 key 复制到 void 系统中。
void 更新了内核之后需要更新 grub 的配置。
# mount /dev/sda3 /mnt
# vi /mnt/boot/grub.cfg
注意这里的 boot 目录不在根目录的子卷内,否则 grub 支持有问题。
另外 void 缺省使用隐私扩展的 IPV6 地址,这在 OpenStack 里面无法被路由,因此需要修改 dhcpcd.conf
设置 slaac hwaddr
。
在 arch 系统中,可以使用
sudo grub-reboot 'Void Linux'
来让系统在重启之后进入到 void 系统。但由于文件系统是 btrfs,grub 无法直接修改,使得之后每次
启动都会在 void 系统中。解决方法是在 void 中安装 grub 工具,不执行 grub-install
安装。
然后用 grub-editenv
修改 arch 的启动设置。
pluto@void ~ > sudo mount /dev/sda3 /mnt
pluto@void /mnt/boot/grub > grub-editenv grubenv set next_entry= 'Arch Linux'
这样就可以在重启后进入 arch 系统。
手持拍摄的视频总会抖动,使用 Linux 的现成工具就可以进行修补。
首先将视频转换为图像文件:
$ ffmpeg -i ../INPUT.MP4 DSC_%03d.png
从图像中找到一个子图像用来对比,尽量小一点的图像便于快速定位,但必要要保证
图像有一定的特点,可以在视频中唯一定位。
接下来利用 ImageMagick 进行子图像的定位:
$ compare -metric RMSE -subimage-search big.png sub.png k.png
其中命令中的最后一个参数是具有定位信息的结果图像,在这里可以忽略。
编写一个简单的命令脚本将每次的定位结果写入到文件中:
$ for i in DSC_* ; do compare -metric RMSE -subimage-search $i sub.png k.png 2>> log.txt; echo >> log.txt; done
$ cat log.txt | awk '{print $4}' > pos.txt
pos.txt 中的坐标值就是子图像在每个视频帧的位置,然后编写一个小脚本对视频帧进行
剪裁。这里我用了 python 以增加扩展性,也许将来可以把全部内容写到一个程序中。
最后用 ffmpeg 将图像再编码成视频。
$ ffmpeg -f image2 -i pic%03d.png test.mp4
校园一卡通是 Mifare 卡,一直想了解它的原理……
MIFARE 是 NXP 公司的商标,主要用于利用 NFC 技术通信的低成本ID/IC卡。
网上找相关标准比较复杂,还是从工程的角度入手。淘宝购入 ACR122U 芯片
的读卡器,然后用相关软件对卡进行操作来增加对卡的认识。
读卡器到手之后在 Linux 下面无法正常工作,dmesg 显示错误消息:
NFC: Couldn't poweron the reader (error -11)
以为是内核的问题,更新了 Debian 到最新版之后问题依旧。
换个思路,在 ACS 下载了最新的驱动,并安装相关软件:
# dpkg -i libacsccid1_1.1.5-1~bpo9+1_amd64.deb
# apt-get install pcscd pcsc-tools
结果运行 pcsc_scan 之后,读卡器的红灯亮起,竟然开始工作了。
不过这个软件读到的只是卡的属性,并看不到具体的数据,继续安装了 nfc 软件
# apt-get install libnfc5 libnfc-bin libnfc-dev
使用 nfc-list 也只能读到ATQA(属性),UID(第一扇区前四个字节)和SAK(?)。
还需要下载一个强力的软件 mfoc,通过暴力破解获取数据。下载源码编译之后,运行
就可以将数据完整读出了,但由于是暴力破解,所以需要的时间比较长。感觉
读一张卡片要 10 分钟左右,比 Windows 下面的软件要快一些,也更加稳定。
mfoc 读出数据的过程也把密钥获得了,将密钥保存到文件中,下次读卡的时候就会
快一些:
# ./mfoc -f keys.txt -O mycard.mfd
目前还有一个疑问,不同的校园卡用的密钥竟然不一样,这样如何管理呢?也许
密钥不唯一,暴力破解的时候就有可能得到不同的值,实际密钥会是不同的一个。
另外如果用校园卡做门禁,只要用到 UID 部分就可以,也不需要破解,速度很快。
由于我比较习惯使用 latex 写自己的各种文档,而提交的时候又往往需要 Word 格式,
有一个合适的转换器就变得非常必要了。
将 latex 转换成 Word 格式的需求不断涌现,印象比较深的是 2011 年提交文稿的那次。
当时也是找了很多工具,最后使用了 tex4ht 工具,将文稿转换为 html,然后用自己
写的一些过滤脚本进行处理,最后才导入 Word。整个过程是比较痛苦而且错误百出的,
结果直到今天依然没有非常理想的工具。
最近又有了难以躲过的转换格式需求,pandoc 软件进入了我的视野,通过查看手册
发现 pandoc 简直是一个万能转换器,而我只是尝试了对 doc 的支持。
用它转换文件格式非常容易,不用配置脚本也没有复杂的开关选项。对于转换 latex
到 docx 只要如下的命令:
$ pandoc -f latex -t docx source.tex -o output.docx
不用解释就可以了解这个命令的使用方法,而且给我的第一感觉就是非常的快,甚至
比使用 latex 生成 pdf 文件的速度还快。唯一发现的问题是表格支持不太好,但表格
在我的文档中使用不多,手动调整一下就好了。
这次的主要任务是转换科技论文,pandoc 又一次给我了惊喜,大部分公式和图片都
很好的进行转换,只有几个问题需要关注:
pdf 格式的文件支持不好,必须先转换为 png 格式。
matrix 公式支持不好
公式里面自定义的 argmax 命令不支持
公式和图没有编号
感觉 pandoc 并没有使用 latex 作为后端,而是用自己的解释器来分析文档,因此
稍微复杂一些的 latex 指令就无法支持了。虽然还没有尝试,估计 pgf 系列包都
不会支持的很好。
没有编号的问题比较大,交叉引用全部都失效了,这次我都通过手工修改了。网上看到
好像有过滤器可以做类似的事情,但语法格式都不是标准的 latex,我就只好放弃了。
我开始以为参考文献是不被支持的,结果一个简单的过滤器就可以解决一半问题。
$ sudo apt-get install pandoc-citeproc
$ pandoc -f latex -t docx source.tex --bibliography= mybib.bibtex -o output.docx
这样是可以生成参考文献,但是格式不符合要求。经过网络搜索才知道还需要一个
格式描述文件(citation style language)ieee.csl 。下载这个文件之后用如下命令就可以
搞定参考文献了。
$ pandoc -f latex -t docx source.tex --bibliography= mybib.bibtex --csl= ieee.csl -o output.docx