Eww 配置完全指南:用 yuck 语言编写 ElKowars wacky widgets 窗口与控件
Eww 配置完全指南用 yuck 语言编写 ElKowars wacky widgets 窗口与控件【免费下载链接】ewwElKowars wacky widgets项目地址: https://gitcode.com/gh_mirrors/ew/eww本篇指南以 EwwElKowars wacky widgets官方配置文档为核心系统讲解如何用其自带的yuck配置语言从零编写窗口window与控件widget包括defwindow各属性几何、锚点、堆叠、WM 交互、defwidget自定义控件、四类变量的定义与更新、literal动态控件、窗口 ID 与参数--arg、for列表渲染以及配置文件的拆分管理。读完本文你将能够独立编写一套可运行、可维护、可动态更新的 Eww 桌面配置并理解配置解析与执行的底层机制。配置基础文件位置、yuck 与 CSS/SCSSEww 使用自研的yuck语言声明控件结构与内容、窗口的几何/位置/行为以及控件中使用的状态与数据。yuck基于 S 表达式S-expressions熟悉 Lisp 类语言的话会非常容易上手使用 Vim 时可借助 yuck.vim 获得编辑器支持使用 VSCode 时可安装 yuck-vscode 扩展获得语法高亮与格式化官方还推荐使用 parinfer 来简化 S 表达式的括号维护。样式方面Eww 使用 CSS 或 SCSS 定义外观。需要特别注意的是Eww 依赖 GTK 自身的 CSS 引擎因此虽然支持 Web 上相当一部分 CSS但并非全部支持——例如部分动画特性以及绝大多数布局相关属性flexbox、float、绝对定位、width/height都不受支持。布局应交给box等容器控件完成。开始前需要创建两个文件eww.yuck和eww.scss也可以叫eww.css它们必须放在$XDG_CONFIG_HOME/eww目录下通常是~/.config/eww。从源码结构看yuck 配置的解析链路位于 crates/yuck/src/parser/parser.lalrpop词法层面把配置拆分为(、)、[、]、关键字:foo、符号foo、simplexpr{...}表达式与注释语法层面则由Ast枚举crates/yuck/src/parser/ast.rs统一表达列表、数组、关键字、符号与内联表达式供后续配置生成阶段使用。创建第一个窗口defwindow窗口是 Eww 的顶层结构。通过defwindow声明窗口名称、位置、几何与内容。官方示例(defwindow example :monitor 0 :geometry (geometry :x 0% :y 20px :width 90% :height 30px :anchor top center) :stacking fg :reserve (struts :distance 40px :side top) :windowtype dock :wm-ignore false example content)这里定义了一个名为example的窗口内容为文本example content。保存后即可用下面的命令打开窗口eww open example窗口定义在源码中的结构体为 crates/yuck/src/config/window_definition.rs 中的WindowDefinition它包含名称、参数列表、几何、堆叠、monitor、内容 widget、是否可缩放、失焦关闭以及后端选项等字段解析时通过expect_key_values()读取:monitor、:resizable、:stacking、:geometry、:unfocus-close等键值对。defwindow 通用属性属性说明monitor窗口显示在哪个显示器上细节见下文geometry窗口的几何信息unfocus-close窗口失去键盘焦点时是否自动关闭此外WindowDefinition还支持resizable属性未指定时默认true见eval_resizable实现。monitor 属性monitor字段支持以下取值对应 crates/yuck/src/config/monitor.rs 中的MonitorIdentifier枚举字符串primaryEww 尝试识别主显示器在 Wayland 上可能失败整数显示器索引显示器名称字符串包含显示器匹配器 JSON 数组的字符串例如[primary, HDMI-A-1, PHL 345B1C, 0]。Eww 会按顺序尝试匹配从而支持优雅的回落fallback。geometry 属性属性说明x,y窗口位置可用px或%单位相对于anchorwidth,height窗口尺寸可用px或%单位anchor窗口锚点取center或top/center/bottom与left/center/right的组合源码中几何由 crates/yuck/src/config/window_geometry.rs 的WindowGeometryDef表示anchor_point、offset、size三部分。锚点解析AnchorPoint::from_str接受center或形如top left的两个词且不区分left top与top left的书写顺序。坐标单位由 crates/yuck/src/value/coords.rs 的NumWithUnit处理支持px省略单位时默认px与%两种%按容器尺寸比例换算例如55.5%是合法的百分比写法。后端专属属性X11 与 Wayland依据运行环境不同defwindow还提供不同的额外属性源码统一在 crates/yuck/src/config/backend_window_options.rs 的BackendWindowOptionsDef::from_attrs中解析X11 与 Wayland 选项共存于同一个配置结构中。X11属性说明stacking窗口在堆栈中的位置取值fg、bgwm-ignore窗口管理器是否忽略该窗口适合 dashboard 类、无需与其他窗口交互的控件。注意开启后部分其他属性将不生效。取true或falsereserve指定窗口管理器为窗口预留空间的方式常用于不应遮挡其他窗口的 barwindowtype窗口类型窗口管理器据此决定处理方式。取值normal、dock、toolbar、dialog、desktop。默认指定了reserve时为dock否则为normal从源码看X11 侧还支持sticky属性让窗口粘在所有工作区wm-ignore的默认值由是否指定windowtype/reserve决定。reserve使用(struts :distance 40px :side top)形式其中side支持left/right/top/bottom以及单字母缩写l/r/t/bdistance为长度值X11WindowType的完整取值还包括utility、notification源码 crates/yuck/src/config/backend_window_options.rs。Wayland属性说明stacking窗口在堆栈中的位置取值fg、bg、overlay、bottomexclusive合成器是否自动为窗口预留空间取true或false若为true:anchor必须包含centerfocusable窗口是否可被聚焦需要使用键盘的控件必须开启。取值none、exclusive、ondemandnamespace设置 eww 使用的 Wayland layersurface 命名空间接受字符串值stacking的完整取值含fg/bg/bottom/overlay及对应的Foreground/Background/Bottom/Overlay枚举定义在 crates/yuck/src/config/window_definition.rs。第一个自定义控件defwidget接下来为窗口添加实际内容。官方示例(defwidget greeter [?text name] (box :orientation horizontal :halign center text (button :onclick notify-send Hello Hello, ${name} Greet)))在窗口定义中调用该控件(defwindow example ; ... values omitted (greeter :text Say hello! :name Tim))逐步解读创建名为greeter的控件接收两个属性text与name?text表示text属性是可选的省略时其值为空字符串name属性必须提供。属性声明的解析实现在 crates/yuck/src/config/attributes.rs 的AttrSpec::from_ast符号以?开头即标记为可选否则为必填。控件体内使用box并设置若干属性。一个控件定义只能包含一个子控件——否则 Eww 无法确定应该垂直还是水平排列、如何留间距因此多个子元素必须用box之类的容器包裹。box内部引用了传入的text属性以及一个按钮按钮的onclick中用字符串插值语法${name}引用传入的name这让你可以在字符串内方便地引用任意变量——${...}内还有更多能力见表达式语言。之后像使用内置控件一样调用greeter并提供所需属性即可。内置控件的完整清单见控件文档。控件定义WidgetDefinition的解析在 crates/yuck/src/config/widget_definition.rs除参数列表外源码还会校验控件体是否超过一个子控件若超出一个会给出明确诊断提示用box包裹。在控件中渲染子元素children配置变大后你可能会把通用功能拆成可复用的包装控件。Eww 允许自定义控件像box、button等内置控件一样接收子元素使用children占位符(defwidget labeled-container [name] (box :class container name (children)))然后按预期使用(labeled-container :name foo (button :onclick notify-send hey ho click me))还可以通过nth属性引用特定位置的子元素构造更复杂的结构(defwidget two-boxes [] (box (box :class first (children :nth 0)) (box :class second (children :nth 1))))children与for在源码中都是WidgetUse的特殊变体crates/yuck/src/config/widget_use.rsChildrenWidgetUse携带可选的nth_expr表达式for则是带元素变量名、来源表达式与循环体的LoopWidgetUse。添加动态内容四类变量控件中显示时间、日期等动态数据需要用到变量。这些用户自定义变量在所有控件中全局可见变量一旦变化控件中的值会立即更新。变量共有四类基础变量basic、轮询变量polling、监听变量listening与内置的 magic 变量。基础变量defvar(defvar foo initial value)这是最简单的变量类型永远不会自动变化只能通过命令显式更新eww update foonew value适合变化频率极低、或由外部脚本触发变化的场景也可以让 Eww 内的按钮通过把onclick设为eww update ...来改变控件显示内容。源码 crates/yuck/src/config/var_definition.rs 显示defvar的格式为(defvar name initial-value)initial_value最终以DynVal形式存储。轮询变量defpoll(defvar time-visible false) ; 用于下方变量的 :run-while 属性 ; 当该变量变为 true 时轮询启动并按给定间隔更新 (defpoll time :interval 1s :initial initial-value ; 可选默认启动时立即轮询一次 :run-while time-visible ; 可选默认 true date %H:%M:%S)轮询变量以固定间隔重复运行提供的 shell 脚本是最常用的变量类型适合反复获取的快速数据时间、日期、待更新的软件包、天气、电池电量等。:initial可指定初始值避免 Eww 启动时等待命令结果从而加快启动速度外部更新轮询变量与基础变量一样使用eww update也可以用eww poll 变量名在常规间隔之外甚至变量完全没在运行时强制轮询一次。源码 crates/yuck/src/config/script_var_definition.rs 中PollScriptVar的结构与之一一对应interval通过as_duration解析支持1s、500ms等格式run_while_expr缺省时默认为字面量true:initial缺省时初始值为空字符串。命令以反引号包裹的 shell 脚本形式存储VarSource::Shell。监听变量deflisten(deflisten foo :initial whatever tail -F /tmp/some_file)监听变量可能是最容易混淆的一种它只运行一次脚本然后持续读取其输出每当脚本输出新的一行变量值就更新为该行。上面例子中foo初始为whatever每当/tmp/some_file追加新行时随之更新。当你有能自行监控某个值的脚本、希望操作发生时立即生效时监听变量非常合适。音量、亮度、运行时增删的工作区、当前聚焦桌面/标签的监控等是最常见的用例。这类变量尤其高效条件允许时应优先使用。典型例子xprop -spy -root _NET_CURRENT_DESKTOP每次当前桌面变化时输出当前聚焦桌面playerctl --follow metadata --format {{title}}监控当前播放的歌曲。ListenScriptVar同上文件结构更简单只包含名称、命令字符串与可选的初始值缺省初始值为空字符串。内置 magic 变量除了自定义变量Eww 直接提供了一些开箱即用的值例如 CPU 和 RAM 使用率。这些值大多以 JSON 形式存放可配合表达式语言的 JSON 访问语法读取。全部 magic 变量列表见 magic-vars.md。magic 变量的具体实现位于 crates/eww/src/config/inbuilt.rs示例配置 examples/eww-bar/eww.yuck 中也有典型用法如{EWW_RAM.used_mem_perc}、{round((1 - (EWW_DISK[/].free / EWW_DISK[/].total)) * 100, 0)}。动态生成控件literal有时需要动态改变的不仅是文本、值或颜色而是整个控件结构——例如展示数量未知的列表如通知、或以更复杂方式改变控件结构。这时可以使用 Eww 最强大的特性之一literal控件。(defvar variable_containing_yuck (box (button foo) (button bar))) ; 然后在你的控件内部使用 (literal :content variable_containing_yuck)literal接收一个字符串通常存放在变量中该字符串包含一棵完整的 yuck 控件树Eww 读取后渲染出对应控件每当内容变化控件都会重新渲染。需要注意literal并非高效务必只在必要时使用其实现见 crates/eww/src/widgets/widget_definitions.rs运行时把内容作为新的 yuck 文本解析load_yuck_str再按常规流程构建 GTK 控件树。窗口参数与 ID一份配置多个实例某些场景下需要让同一份窗口配置服务于多个窗口这时就要用到参数arguments与 IDids。窗口 IDID 可通过open命令的--id指定默认取窗口配置名。ID 允许你同时开启多个同名窗口实例例如eww open my_bar --screen 0 --id primary eww open my_bar --screen 1 --id secondary使用open-many时遵循下面的结构同样地未给 ID 时默认使用窗口配置名eww open-many my_config:primary my_config:secondary注意上面的例子没有设置screen——它通过--arg系统传入详见下文。窗口参数--arg仅靠 ID 可能还不够比如希望 1080p 与 4K 显示器使用不同 class或在不同位置/尺寸打开窗口——这时就需要参数。请注意这些参数是常量CONSTANT窗口打开后无法更新。在窗口里定义参数与在控件中完全相同(defwindow my_bar [arg1 ?arg2] :geometry (geometry :x 0% :y 6px :width 100% :height { arg1 small ? 30px : 40px } :anchor top center) :stacking bg :windowtype dock :reserve (struts :distance 50px :side top) (my_widget :arg2 arg2))这里有两个参数arg1与arg2后者可选。打开窗口时必须通过open的--arg选项提供非可选参数eww open my_bar --id primary --arg arg1some_value --arg arg2another_valueopen-many的写法如下# 注意--arg 选项必须放在所有窗口名之后 eww open-many my_bar:primary --arg primary:arg1some_value --arg primary:arg2another_value用这种方法可以在每个窗口的参数里定义screen、anchor、pos、size效果等同于在open命令中直接给出--screen、--anchor等选项。这些“特殊”参数设置方式略有不同但全部可以被--arg覆盖id— 若参数列表中包含id它会被设为--id指定的值未指定时取配置名可用于通过 eww 命令关闭当前窗口screen— 若指定了screen它会被设为--screen的值这样其他控件也能访问屏幕相关信息。参数解析在命令行层面由 crates/eww/src/opts.rs 完成--arg的parse_var_update_arg、open-many的parse_window_id_args分别支持varvalue与window_id:varvalue两种语法screen/pos/size/anchor/duration会被提取为窗口初始化信息其余进入参数表。窗口打开时crates/eww/src/window_arguments.rs 的get_local_window_variables会校验必填参数缺失或出现意外参数都会报错。open-many 中 --arg 的更多细节由于open-many的--arg处理机制不必为每个参数指定 ID未指定 ID 的参数会应用到所有窗口例如eww open-many my_bar:primary my_bar:secondary --arg gui_sizesmall这样所有 bar 使用相同配置。此外即便窗口没有指定 IDID 默认为窗口配置名仍可针对该窗口单独设置参数直接用窗口配置名即可eww open-many my_primary_bar --arg my_primary_bar:screen0用 for 从 JSON 生成控件列表要展示一组值可以用for元素它基于 JSON 数组生成一组元素并填入容器。(defvar my-json [1, 2, 3]) ; 然后在你的控件内部使用 (box (for entry in my-json (button :onclick notify-send click button ${entry} entry)))这在很多场景都很实用例如从工作区的 JSON 表示生成工作区列表。多数情况下它可以替代literal并且应当优先使用。for的语法在 crates/yuck/src/config/widget_use.rs 的LoopWidgetUse中定义依次解析元素变量名、in关键字、来源表达式与循环体来源表达式必须是可求值为 JSON 数组的 simplexpr。关于更高级数据结构的声明与使用参见数据结构示例。拆分配置include 与独立配置目录随着时间推移配置会越来越大Eww 支持把配置拆分成多个文件有两种方式使用 include(include ./path/to/your/file.yuck)任何 yuck 文件都可以通过include指令导入其他 yuck 文件的内容。源码层面crates/yuck/src/config/toplevel.rs 的Include会递归加载被包含文件并合并其顶层声明defvar/defpoll/deflisten/defwidget/defwindow/include均被递归处理变量重复定义会报错。使用独立的 eww 配置目录如果想更进一步分离不同控件可以在任意位置新建 eww 配置文件夹然后通过给每个命令加--config /path/to/your/config/dir标志让 eww 使用该配置目录eww --config /path/to/your/config/dir ...务必在所有eww 调用中都带上该标志包括eww kill、eww logs等。这会启动一个独立的 eww 守护进程实例与主配置拥有独立的日志和状态。--config是全局选项见 crates/eww/src/opts.rs 的RawOpt::config字段与--debug、--force-wayland、--logs、--no-daemonize、--restart一样可作用于所有子命令。与配置相关的常用 CLI 命令除本文提到的eww open、eww open-many、eww update、eww poll、eww kill、eww logs外结合 crates/eww/src/opts.rs 中的子命令定义配置调试阶段常用的还有命令作用eww reload别名r重新加载配置与 CSSeww close-all别名ca关闭所有窗口但不杀掉守护进程eww state-a/--all打印当前打开窗口用到的所有变量eww get 变量名获取某个变量的当前值eww list-windows列出已定义的窗口eww active-windows按window_id: window_name格式列出活动窗口eww debug打印 eww 视角下的控件结构排查配置解析问题或提交 bug 时很有用eww graph以 graphviz dot 格式打印作用域图结构完整示例官方 eww-bar仓库中的 examples/eww-bar/eww.yuck 是一个综合示例融合了本文大部分知识点用defwidget组合出workspaces、music、metric等可复用控件用deflisten监听 playerctl 播放状态用defpoll轮询音量与时间defwindow定义 dock 窗口并通过:reserve (struts ...)为顶栏预留空间onclick中调用wmctrl、amixer等外部命令与 Eww 交互。配合 examples/eww-bar/eww.scss 中的 SCSS 样式可以作为从零搭建自己状态栏的起点。【免费下载链接】ewwElKowars wacky widgets项目地址: https://gitcode.com/gh_mirrors/ew/eww创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考