为中文输入法 fcitx 提供 Wayland 桌面通知功能的 lua 脚本
a fcitx5 addon lua script that enables wayland notification
zping
1119 字
2025-04-11 11:11 +0000
本篇中脚本依赖特定应用,仅仅提供一个实现的思路供路人参考
特定的依赖,包括 libnotify 和 mako ,特别是 makoctl list 输出格式的更新,已经让我不得不专门对本篇进行了一次更新。 这次更新可以参考这个gist。
之前写过一个 Lua
脚本,通过 fcitx5
插件(addon
)功能,让其在输入法状态切换时显示桌面通知。这两天我在更新自己工作环境,除了新装机以外,也更新了日常使用的操作系统和桌面环境。所以之前的脚本也需要更新。
这个功能的目的和由来之前的分享里已经提过。主要是让自己能够更好地感知输入法的状态。特别是有三四种输入法时,切换时提示醒目一点不是坏事。
我这次选择了 Wayland/Sway
这个桌面环境,通知服务主要选择是 mako
和 swaync
。本来一开始我测了一下 mako
,发现 mako
功能有点少,想多整点花活,于是又装了 swaync
。然而,我发现 swaync
基于 gtk
实现,其 css
和日常 W3C
的 css
e 。调了半天连最简单的文本居中都没有调出来,怒而改回用 mako
。
最后配置路径仍和之前差不多,插件配置文件的具体路径是 ~/.local/share/fcitx5/addon/notify.conf
[Addon]
Name=Notify send Lua
Comment=Lua script for displaying notification
Category=Module
Type=Lua
OnDemand=False
Configurable=False
Library=notify.lua
[Addon/Dependencies]
0=luaaddonloader
还有 Lua
脚本的具体路径则是 ~/.local/share/fcitx5/lua/notify/notify.lua
local fcitx = require("fcitx")
local notify_app_name = "fcitx5"
function deepcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[deepcopy(orig_key)] = deepcopy(orig_value)
end
else -- number, string, boolean, etc.
copy = orig
end
return copy
end
local args = {
"${HOME}/.nix-profile/bin/notify-send",
"--print-id",
"-a", "\"" .. notify_app_name .. "\"",
"-t", "2000",
"\"" .. notify_app_name .. "\""
}
fcitx.watchEvent(fcitx.EventType.KeyEvent, "caps_lock")
fcitx.watchEvent(fcitx.EventType.SwitchInputMethod, "switch_im")
-- 获取指定 LED 的当前状态
local function get_led_state(led_name)
local file = io.open("/sys/class/leds/input0::"..led_name.."/brightness", "r")
if not file then return nil end
local state = file:read("*n") -- 读取数值
print(state)
file:close()
return state == 1 and "ON" or "OFF"
end
local function notify(msg_body)
local notify_id_file = "/tmp/last-" .. notify_app_name .. "-notify-id"
local notify_id = nil
local file = io.open(notify_id_file, "r")
local cargs = deepcopy(args)
table.insert(cargs, msg_body)
if file then
notify_id = file:read("*l")
file:close()
end
local has_notify_id = false
if notify_id then
-- 检查 makoctl 列表中是否存在该通知
local handle = io.popen("${HOME}/.nix-profile/bin/makoctl list")
local mako_output = handle:read("*a")
handle:close()
if mako_output and mako_output:find('Notification ' .. notify_id) then
has_notify_id = true
end
end
-- 发送通知
local new_notify_id = nil
if notify_id and has_notify_id then
table.insert(cargs, 2, notify_id)
table.insert(cargs, 2, "-r")
local cmd = table.concat(cargs, " ")
local handle = io.popen(cmd)
new_notify_id = handle:read("*l")
handle:close()
else
-- 发送新通知
local cmd = table.concat(cargs, " ")
local handle = io.popen(cmd)
new_notify_id = handle:read("*l")
handle:close()
-- 保存新通知 ID
if new_notify_id then
file = io.open(notify_id_file, "w")
if file then
file:write(new_notify_id)
file:close()
end
end
end
end
function caps_lock(sym, state, release)
if sym == 65509 and release then
local capslock_state_msg = "\"CapsLock <b>" .. get_led_state("capslock") .. "</b>\""
print(capslock_state_msg)
notify(capslock_state_msg)
-- print(string.format("change state of CapsLock: %s", enable))
end
end
function switch_im()
local msg = string.format("\"InputMethod switched to <b>%s</b>\"", fcitx.currentInputMethod())
print(msg)
notify(msg)
end
既然过了几年,还是脚本就再增加一些功能。这里主要就是增加 --replace-id
参数。这样在频繁切换时,会替换掉之前消息框,不会出现之前一堆消息框堆在一起的情况。当然,这个实现其实用 swaync
做比 mako
简单。因为 swaync
的逻辑是只要带上 replace-id
那返回的消息 ID
就是 replace-id
,和之前发送的消息的 ID
没有任何关系。因此通知脚本无需追踪先前通知的状态。 mako
的行为则是它拿到的 replace-id
必须是有效的,否则它会返回新 ID
,这显著增加了实现复杂度。
脚本依赖包括 mako
libnotify
fcitx5-lua
这三项。还有两点要注意的,一点是我并不熟悉 lua
,五年只写过四个。这样的情况所以像那个 deepcopy
函数,直接拿人工智能写的。这个实现可能并不合理。另一点是这个查询大小写状态我直接去读 /sys/class/leds/input0::capslock/brightness
,我也没有花太多时间想没有更好的实现,也是直接让人工智能写的。反正现在能跑通。
这次我的配置已经用上了 nix home manager
,相关的配置是写在 home.nix
里面的。这次涉及的配置片断是这样的:
home.file = {
".local/share/fcitx5/addon".source = config.lib.file.mkOutOfStoreSymlink ./fcitx5-addon;
".local/share/fcitx5/lua".source = config.lib.file.mkOutOfStoreSymlink ./fcitx5-lua;
};
这篇有点长,先到这里。