You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改XMobar的Kbd监控插件实现点击循环切换布局

XMobar点击切换键盘布局实现方案

方案一:自定义MyOwnKbd插件(纯Haskell实现)

如果希望把点击逻辑和插件业务完全绑定,最可靠的方式是自定义插件,通过Haskell维护布局切换状态,无需外部脚本。

插件代码实现

在XMobar配置文件中添加以下代码(需确保导入必要模块):

import Xmobar
import System.Process (callProcess)
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.List (elemIndex)

-- 自定义插件数据类型,存储布局列表和当前索引状态
data MyOwnKbd = MyOwnKbd [(String, String)] (IORef Int)

instance Runner MyOwnKbd where
    -- 初始化插件:创建索引引用,设置初始布局,返回点击处理函数
    run (MyOwnKbd layouts ref) _ updateDisplay = do
        let (initLayout, _) = layouts !! 0
        callProcess "setxkbmap" (splitLayout initLayout)
        refreshDisplay layouts ref updateDisplay
        return $ handleClick layouts ref updateDisplay

    alias _ = "mykbd"

-- 解析布局字符串(如"ru(phonetic)"拆分为["ru", "-variant", "phonetic"])
splitLayout :: String -> [String]
splitLayout s = case break (== '(') s of
    (lang, "") -> [lang]
    (lang, '(':rest) -> lang : "-variant" : init rest

-- 更新状态栏显示当前布局别名
refreshDisplay :: [(String, String)] -> IORef Int -> (String -> IO ()) -> IO ()
refreshDisplay layouts ref update = do
    idx <- readIORef ref
    let (_, alias) = layouts !! idx
    update alias

-- 点击事件处理:循环切换到下一个布局
handleClick :: [(String, String)] -> IORef Int -> (String -> IO ()) -> Button -> IO ()
handleClick layouts ref update _ = do
    currentIdx <- readIORef ref
    let nextIdx = (currentIdx + 1) `mod` length layouts
        (targetLayout, _) = layouts !! nextIdx
    -- 执行布局切换
    callProcess "setxkbmap" (splitLayout targetLayout)
    -- 更新状态索引和显示
    writeIORef ref nextIdx
    refreshDisplay layouts ref update

-- 构造插件的便捷函数
myKbd :: [(String, String)] -> IO MyOwnKbd
myKbd layouts = MyOwnKbd layouts <$> newIORef 0

配置使用

在XMobar的commands列表中添加插件,模板中引用别名:

commands = [
    -- 其他插件...
    Run $ myKbd [("it", "IT"), ("ru(phonetic)", "RU")]
    ]

template = "%mykbd% | 其他状态栏元素..."

点击%mykbd%区域时,会在指定布局列表中循环切换,状态由插件内部维护,无需外部依赖。


方案二:原生Kbd插件+内嵌Haskell脚本

如果不想编写完整自定义插件,可以利用XMobar的<action>标签,通过内嵌Haskell脚本实现切换逻辑,无需额外外部脚本文件。

配置示例

保留原生Kbd插件,修改模板为:

commands = [
    Run $ Kbd [("it", "IT"), ("ru(phonetic)", "RU")]
    ]

template = "<action=`runhaskell -e \"import System.Process; import Data.List; getCurrentLayout = head . words <$> readProcess \\\"setxkbmap\\\" [\\\"-query\\\"] \\\"\\\"; let layouts = [\\\"it\\\", \\\"ru(phonetic)\\\"]; getCurrentLayout >>= \\l -> let Just idx = elemIndex l layouts; next = layouts !! ((idx+1) `mod` length layouts) in callProcess \\\"setxkbmap\\\" (splitLayout next) where splitLayout s = case break (== '(') s of (lang,\\\"\\\") -> [lang]; (lang,'(':rest) -> lang : \\\"-variant\\\" : init rest\"`>%kbd%</action>"

逻辑说明

点击%kbd%区域时,执行内嵌的Haskell代码:

  1. 调用setxkbmap -query获取当前布局
  2. 在指定布局列表中找到当前布局的索引
  3. 计算下一个布局的索引(循环)
  4. 调用setxkbmap切换到目标布局,原生Kbd插件会自动同步显示

关键说明

原生Kbd插件仅负责监控布局变化并显示,无法直接将点击事件反馈到自身业务逻辑,因此必须通过自定义插件或内嵌脚本的方式实现点击切换需求。

内容的提问来源于stack exchange,提问作者Enlico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 04:52:03