DMS+Stylix主题切换

作者:Erina
6 分钟阅读 701 字

目录

  1. dms壁纸切换钩子
  2. 切换specialisation
  3. 具体实现
  4. 注意事项
  5. 成果展示

前几日在B站上刷到这样一个视频

up展示了一个通过定义不同的specialisation在运行时便携的切换主题配色的方案,看的着实让人羡慕, 于是我仿照up主的实现,结合DMS的壁纸选择器,实现了一个通过dms切换壁纸时同步切换specialisation的功能, 写的还挺有成就感的,所以在这里分享一下。

dms壁纸切换钩子

dms的源代码里有一个示例插件,就是在切换壁纸时运行钩子,Wallpaper Watcher Daemon

我们可以通过flake input得到这个插件的源码,使用homemanager定义我们要运行的脚本:

home.nix
programs.dank-material-shell.plugins.wallpaperWatcherDaemon = {
src = inputs.dms + "/quickshell/PLUGINS/WallpaperWatcherDaemon";
settings.scriptPath = "${themeSwitch}/bin/erinite-theme-switch";
};

切换specialisation

切换specialisation需要root权限,在用户态运行的dms显然是不能直接切换的, 这里我的解决方案使用systemd + polkit

我们定义dms切换壁纸后使用systemctl启动切换某个主题的服务:

systemctl start erinite-theme-switch@<theme>.service

polkit能够控制特定的用户启动特定的模板服务。我们直接开放权限给主题切换,就能实免权限切换主题。

通过erinite-theme-switch@.service这个服务里的 ExecStart 指向系统侧打包出切换脚本, 如果不用 systemd, 就需要sudoers规则,通常更粗糙,也更难和桌面程序、DMS 回调这种非交互场景配合。 systemd + polkit的好处是授权范围小、可审计、适合 GUI/IPC 触发。

具体实现

具体的实现我们定义这个themeSwitch,接上上面dms定义的插件:

home.nix
themeSwitch = pkgs.writeShellApplication {
name = "erinite-theme-switch";
runtimeInputs = with pkgs; [
coreutils
systemd
];
text = ''
set -euo pipefail
wallpaper="''${1-}"
file_name="$(basename -- "$wallpaper")"
choice="''${file_name%.*}"
unit="$(systemd-escape --template=erinite-theme-switch@.service "$choice")"
systemctl start "$unit"
'';
};

然后在nixos系统配置里定义这两个内容:

configuration.nix
let
switchTheme = pkgs.writeShellScript "erinite-theme-switch-system" ''
set -eu
theme="''${1:?missing theme name}"
exec /nix/var/nix/profiles/system/specialisation/$theme/bin/switch-to-configuration switch
'';
in {
security.polkit = {
enable = true;
extraConfig = ''
polkit.addRule(function(action, subject) {
var unit = action.lookup("unit");
var verb = action.lookup("verb");
if (
action.id == "org.freedesktop.systemd1.manage-units" &&
unit && unit.match(/^erinite-theme-switch@.+\.service$/) &&
verb == "start"
) {
return polkit.Result.YES;
}
});
'';
};
systemd.services."erinite-theme-switch@" = {
description = "Switch to NixOS theme specialisation %I";
serviceConfig = {
Type = "oneshot";
ExecStart = "${switchTheme} %I";
};
};

注意事项

这里的脚本是比较简单且理想化的,我默认壁纸图片名和主题颜色名为同一个,可以看到我的代码:

Terminal window
file_name="$(basename -- "$wallpaper")"
choice="''${file_name%.*}"

这里选定的主题就是壁纸名去掉拓展,这是因为这些都是我通过nix定义好的, 生成的主题和壁纸名是相同的,dms壁纸扫描的路径也是我定义的壁纸的目录。

如果真正要在别人的配置中应用, 你要么修改这里的实现, 要么去查看一下我源码实现的mkWallpapers函数及其配套的代码

成果展示