XMobar可点击功能生效后崩溃,求现代实现方案
问题概述
我要实现可点击XMobar(不止工作区,后续要支持其他插件),通过以下方式给工作区标签添加点击动作:
定义包裹动作的函数:
foo = wrap "<action=`touch ~/foo` button=1>" "</action>"
或等价写法:
foo = xmobarAction "touch ~/foo" "1"
将其与XMobar的PP组合:
myXmobarPP :: PP myXmobarPP = def { ppCurrent = foo . ppCurrent def }
点击当前工作区标签时,touch ~/foo能正常执行,但xmobar进程会直接终止。
已尝试的方法(均存在进程终止问题)
我试过多种方案,点击操作能生效,但都会导致xmobar崩溃:
- 使用
XMonad.Util.ClickableWorkspaces的clickablePP:
在xmobar.hs中把XMonadLog替换为UnsafeXMonadLog,并在xmonad.hs中传入clickablePP:main = xmonad . ewmhFullscreen . ewmh . withEasySB (statusBarProp (unwords ["xmobar", xmobarhs]) (clickablePP myXmobarPP)) toggleStrutsKey $ myConfig - 手动给
ppHidden添加点击动作:
同样替换XMonadLog为UnsafeXMonadLog,自定义clickable函数并组合到ppHidden:clickable ws = "<action=xdotool key super+"++ws++">"++ws++"</action>" myXmobarPP :: PP myXmobarPP = def { ppHidden = white . wrap " " "" . clickable, -- 其他配置... } - 使用
clickableWrap函数:
测试clickableWrap 2 "ciao"会生成<action=xdotool set_desktop 2button=1>ciao</action>,验证xdotool set_desktop 2能正常切换工作区(xdotool工作区从零开始计数),并修改ppHidden为:ppHidden = \x -> clickableWrap ((read x :: Int) - 1) x
以上方法都能实现点击切换工作区,但触发点击后xmobar进程立即终止,手动启动xmobar查看错误仅得到:
xmobar: waitForProcess: does not exist (No child processes)
最小复现配置
xmonad.hs
import XMonad import XMonad.Hooks.EwmhDesktops import XMonad.Hooks.StatusBar import XMonad.Util.ClickableWorkspaces main :: IO () main = xmonad . ewmhFullscreen . ewmh . withEasySB (statusBarProp myxmobar (clickablePP def)) defToggleStrutsKey $ myConfig where myxmobar = "xmobar ~/.config/xmobar/xmobar.hs" myConfig = def { terminal = "urxvt" , modMask = mod4Mask }
xmobar.hs
import Xmobar main :: IO () main = xmobar defaultConfig { overrideRedirect = False , position = BottomSize L 100 25 , commands = [ Run UnsafeXMonadLog ] , sepChar = "%" , alignSep = "}{" , template = "%UnsafeXMonadLog% }{" }
解决方案
问题出在UnsafeXMonadLog和statusBarProp的组合上:statusBarProp会让xmonad启动xmobar进程并监控它,而UnsafeXMonadLog执行外部命令时的进程处理逻辑会和xmonad的进程监控冲突,导致xmobar被误判为异常退出。
修复步骤:
替换启动方式:不要用
statusBarProp,改用statusBar,让xmonad通过管道和xmobar通信,而非直接监控进程:
修改xmonad.hs中的withEasySB部分:main = xmonad . ewmhFullscreen . ewmh . withEasySB (statusBar myxmobar (clickablePP def)) defToggleStrutsKey $ myConfig where myxmobar = "xmobar ~/.config/xmobar/xmobar.hs"statusBar会自动使用管道通信,避免进程监控冲突。保持
UnsafeXMonadLog配置:xmobar.hs中继续使用UnsafeXMonadLog,它是支持<action>标签的必要配置。优化命令执行:如果自定义
<action>中的命令,建议将命令放到后台执行(加&),避免命令阻塞导致xmobar异常:foo = xmobarAction "touch ~/foo &" "1"
原理说明
statusBarProp通过spawnPipe启动xmobar并监控其生命周期,而UnsafeXMonadLog执行<action>中的外部命令时会创建子进程,xmonad的进程监控逻辑可能把这个子进程误当成xmobar本身,当子进程退出时,xmonad会认为xmobar崩溃并终止它。而statusBar的管道通信方式,不会直接监控xmobar进程,而是通过管道传递日志,避免了这个冲突。
内容的提问来源于stack exchange,提问作者Enlico

