Asthestarsfalll
piri
Rust

🚀 Piri is a high-performance Niri extension tool built with Rust. It leverages efficient Niri IPC interaction and a unified event distribution mechanism to provide a robust, state-driven plugin system.

Last updated Jul 4, 2026
78
Stars
11
Forks
4
Issues
0
Stars/day
Attention Score
58
Language breakdown
No language data available.
Files click to expand
README

Piri

English | 中文


Piri 是基于 Rust 开发的 Niri 合成器扩展工具,通过高效的 IPC 交互与统一的事件分发机制,提供稳健的状态驱动插件系统。

核心插件

  • 📦 Scratchpads: 智能隐藏/显示窗口,支持自动捕获或按需启动,跨工作区与显示器跟随(详见 文档
  • 🔌 Empty: 空工作区自动化,切换时自动执行命令(详见 文档
  • 🎯 Window Rule: 规则引擎,基于正则匹配实现窗口自动归位与焦点触发命令(详见 文档
  • 📐 Workspace Rule: 工作区布局管理,提供自动宽度、平铺、对齐、最大化及 EdgePulse 边缘提示(详见 文档, EdgePulse
  • 🔒 Singleton: 单实例保障,确保应用全局唯一(详见 文档
  • 📌 Mark: 窗口标记与快速聚焦,绑定存于内存(详见 文档
  • 📍 Sticky: 浮动窗口跟随,支持跨显示器(详见 文档
  • 📋 Window Order: 按权重自动重排窗口,最小化移动(详见 文档
  • 🍽️ Swallow: 窗口吞噬,子窗口替换父窗口位置(详见 文档

核心特性

  • Rust 高性能:基于 Rust 构建,低内存占用,异步事件处理
  • 高效 IPC 交互:通过 Unix Socket 与 Niri 合成器通信,自动重连,批量指令执行
  • 统一事件分发:单一事件监听器统一接收 Niri 事件,按插件兴趣分发
  • 细粒度事件拆分:自动将 Niri 粗粒度事件拆分为 WindowOpenedWindowChangedWindowToggleFloating 等子事件,插件无需自行追踪窗口状态(详见 文档
  • 状态驱动插件系统:每个插件实现统一的 Plugin trait,支持配置热更新、事件过滤与 IPC 命令处理
  • 配置热重载:修改 piri.toml 后自动生效,无需重启 daemon

窗口匹配机制

Piri 使用统一的窗口匹配机制,支持通过正则表达式匹配窗口的 appidtitle。多个插件(如 windowrulesingletonscratchpads)都使用此机制来查找和匹配窗口。

支持的匹配方式

  • 正则表达式匹配: 支持完整的正则表达式语法
  • 灵活匹配: 支持 app_id 和/或 title 匹配
  • OR 逻辑: 如果同时指定 app_idtitle,任一匹配即可
注意: Window Rule 插件额外支持列表匹配(appidtitle 可以是列表),详见 Window Rule 文档

详细文档: 窗口匹配机制文档

快速开始

安装

使用安装脚本(推荐)

最简单的方式是使用提供的安装脚本:

./install.sh

安装脚本会自动:

  • 构建 release 版本
  • 安装到 ~/.local/bin/piri(普通用户)或 /usr/local/bin/piri(root)
  • 复制配置文件到 ~/.config/niri/piri.toml
如果 ~/.local/bin 不在 PATH 中,脚本会提示你添加到 PATH。

使用 Cargo 安装

# 安装到用户目录(推荐,不需要 root 权限)
cargo install --path .

或者安装到系统目录(需要 root 权限)

sudo cargo install --path . --root /usr/local

安装完成后,如果安装到用户目录,确保 ~/.cargo/bin 在你的 PATH 环境变量中:

export PATH="$PATH:$HOME/.cargo/bin"

可以将此命令添加到你的 shell 配置文件中(如 ~/.bashrc~/.zshrc)。

配置

将示例配置文件复制到配置目录:

mkdir -p ~/.config/niri
cp config.example.toml ~/.config/niri/piri.toml

然后编辑 ~/.config/niri/piri.toml 来配置你的功能。

使用方法

启动 daemon

手动启动

# 启动 daemon(前台运行)
piri daemon
# 更多调试日志
piri --debug daemon

自动启动(推荐)

在 niri 配置文件中添加以下配置,让 piri daemon 在 niri 启动时自动运行:

编辑 ~/.config/niri/config.kdl,在 spawn-at-startup 部分添加:

spawn-at-startup "bash" "-c" "/path/to/piri daemon > /dev/null 2>&1 &"

Shell 自动补全

生成 shell 自动补全脚本:

# Bash
piri completion bash > ~/.bash_completion.d/piri

Zsh

piri completion zsh > ~/.zshcompletion.d/piri

Fish

piri completion fish > ~/.config/fish/completions/piri.fish

插件

Scratchpads

https://github.com/user-attachments/assets/1c40d75b-310a-4503-9d59-92f0479a54d5

快速显示/隐藏常用窗口,支持跨工作区与显示器。特性:动态添加保留手动调整隐藏后移至指定工作区吞入聚焦窗口swallowtofocus)、Sticky 跟随sticky,委托 sticky 插件)、失焦自动隐藏autohideonfocusloss)及非浮动窗口直接聚焦

配置示例

[piri.plugins] scratchpads = true

[piri.scratchpad] default_size = "40% 60%" default_margin = 50 movetoworkspace = "tmp" # 窗口隐藏后自动移动到工作区 tmp

[scratchpads.term] direction = "fromRight" command = "GTKIMMODULE=wayland ghostty --class=float.dropterm" app_id = "float.dropterm" size = "40% 60%" margin = 50

[scratchpads.preview] direction = "fromRight" command = "imv" app_id = "imv" size = "60% 80%" margin = 50 swallowtofocus = true # 显示时自动吞入当前聚焦的窗口

[scratchpads.note] direction = "fromTop" command = "gnome-text-editor" app_id = "org.gnome.TextEditor" size = "50% 40%" margin = 100 sticky = true # 跟随焦点工作区(由 sticky 插件处理)

[scratchpads.calc] direction = "fromBottom" command = "gnome-calculator" app_id = "org.gnome.Calculator" size = "30% 40%" margin = 50 autohideonfocusloss = true # 失去焦点时自动隐藏

注意: 同一个 scratchpad 不能同时启用 stickyautohideonfocusloss

快速使用

# 切换 scratchpad 显示/隐藏 piri scratchpads {name} toggle

动态添加当前窗口为 scratchpad

piri scratchpads {name} add {direction} [--swallow-to-focus]

示例

piri scratchpads mypad add fromRight piri scratchpads mypad add fromRight --swallow-to-focus # 启用 swallow 功能
提示:
- 动态添加的窗口仅在第一次注册时使用默认大小和边距。之后你可以手动调整窗口的大小和位置(边距),插件会自动保留这些调整。
- 如果 scratchpad 已存在,add 命令会自动执行 toggle 操作(显示/隐藏切换)。

详细说明请参考 Scratchpads 文档

Empty

在切换到空 workspace 时自动执行命令,用于自动化工作流程。

参考: 此功能类似于 Hyprland 的 on-created-empty workspace rule

配置示例

[piri.plugins] empty = true

当切换到 workspace 1 且为空时,执行命令

[empty.1] command = "alacritty"

使用 workspace 名称

[empty.main] command = "firefox"

Workspace 标识符:支持使用 workspace 名称(如 "main")或索引(如 "1")来匹配。

详细说明请参考 插件系统文档

Window Rule

基于正则匹配自动归位窗口至指定 workspace,支持焦点触发命令执行。

配置示例

[piri.plugins] window_rule = true

根据 app_id 匹配,移动到 workspace(精确匹配:先 name,后 idx)

[[window_rule]] app_id = ".firefox." openonworkspace = "2"

根据 title 匹配,移动到 workspace,并在获得焦点时执行命令

[[window_rule]] title = ".Chrome." openonworkspace = "3" focus_command = "[[ $(fcitx5-remote) -eq 2 ]] && fcitx5-remote -c"

同时指定 app_id 和 title(任一匹配即可),移动到 workspace(name)

[[window_rule]] app_id = "code" title = ".VS Code." openonworkspace = "browser"

只有 focus_command,不移动窗口

[[window_rule]] title = ".Chrome." focus_command = "notify-send 'Chrome focused'"

focus_command 仅对规则全局执行一次(规则级别,非窗口级别)

[[window_rule]] app_id = "firefox" focus_command = "notify-send 'Firefox focused'" focuscommandonce = true

app_id 作为列表(任意一个匹配即可)

[[window_rule]] app_id = ["code", "code-oss", "codium"] openonworkspace = "dev"

锁定窗口到指定 workspace(末尾 ! 表示锁定,窗口无法被移走)

[[window_rule]] app_id = "firefox" openonworkspace = "1@eDP!"

title 作为列表(任意一个匹配即可)

[[window_rule]] title = [".Chrome.", ".Chromium.", ".Google Chrome."] openonworkspace = "browser"

特性

  • 正则匹配(app_id/title,支持列表与 OR 逻辑)
  • 支持 workspace 名称/索引
  • Workspace 锁定(! 后缀),防止窗口被移走
  • 焦点触发命令,内置去重机制
  • focuscommandonce:规则级单次执行(issue #1
  • 纯事件驱动
详细说明请参考 Window Rule 文档窗口匹配机制文档

Workspace Rule

https://github.com/user-attachments/assets/092a383c-993b-42b5-9b89-b78b0807b436

https://github.com/user-attachments/assets/b48b0465-0101-4298-9935-22f46d1a2658

https://github.com/user-attachments/assets/3b8dc835-473c-4b6e-b684-02a6951b63f9

https://github.com/user-attachments/assets/2e9e2c86-b9ef-44f1-a896-46473b93b417

工作区布局管理,提供自动宽度、平铺、对齐与最大化。内置 EdgePulse 边缘提示(支持动画),聚焦列到达边缘时渲染视觉提示。

配置示例

[piri.plugins] workspace_rule = true

默认配置(应用到所有工作区)

[piri.workspace_rule] auto_width = ["100%", "50%", "33.33%", "25%", "20%"] auto_fill = true # 启用自动对齐 auto_maximize = true # 自动最大化

EdgePulse 边缘提示(支持动画)

[piri.workspacerule.edgepulse] enabled = true animation_enabled = true # 启用动画 animation_style = "pulse" # pulse 呼吸效果,fade 淡入效果 animation_duration = 600 # 动画周期(毫秒) animation_amplitude = 0.8 # 动画强度 animation_repeat = 3 # 每次触发播放次数(0=无限)

工作区特定配置

[workspace_rule.main] auto_maximize = true

[workspace_rule.dev] auto_width = ["100%", ["45%", "55%"], ["30%", "35%", "35%"]] autotile = true # autotile

特性

  • 自动宽度调整:根据窗口数量自动调整窗口宽度
  • auto_tile:自动将新窗口合并到现有列中
  • 自动对齐:窗口关闭后自动对齐到最右侧
  • 自动最大化:单窗口时自动最大化,多窗口时取消最大化
  • EdgePulse 边缘提示:聚焦列到达边缘时渲染视觉提示
  • 工作区感知:每个工作区可独立配置
详细说明请参考 Workspace Rule 文档

Singleton

管理单例窗口,确保全局唯一。切换时聚焦已有实例或启动新进程,适用于浏览器、终端等。

配置示例

[piri.plugins] singleton = true

[singleton.browser] command = "google-chrome-stable"

[singleton.term] command = "GTKIMMODULE=wayland ghostty --class=singleton.term" app_id = "singleton.term"

[singleton.editor] command = "code" app_id = "code" oncreatedcommand = "notify-send '编辑器已打开'"

快速使用

# 切换单例窗口(如果存在则聚焦,不存在则启动) piri singleton {name} toggle

特性

  • 智能检测现有窗口,自动提取 App ID
  • 窗口注册表快速查找
  • 支持创建后执行命令(oncreatedcommand
详细说明请参考 Singleton 文档

Mark

为窗口设置命名标记(如 ab),用于快速聚焦。标记存于内存,重启 daemon 后清空。无需配置文件,启用插件后通过 Niri spawn 绑定即可。

配置示例

[piri.plugins]
mark = true

快速使用

# 无有效绑定时:把当前焦点窗口绑定到名称;已有且窗口仍在:聚焦该窗口
piri mark {name} toggle

强制把当前窗口绑定到名称(覆盖原绑定)

piri mark {name} add

删除该标记

piri mark {name} delete

说明:Piri 无法全局捕获「下一个按键」;若希望少占快捷键,可配合启动器(如 fuzzel)选字母后再调用上述命令。Niri 若支持多键序列或绑定模式,可将多条 piri mark … 收拢在同一前缀下。

详细说明请参考 Mark 文档

Sticky

将当前焦点的浮动窗口设为“跟随窗口”,在你切换焦点工作区时自动跟随。适用于词典、翻译、日志、播放器控制等常驻小窗场景。

配置示例

[piri.plugins]
sticky = true

快速使用

# 设为 sticky(仅同 monitor 跟随)
piri sticky add

设为 sticky(允许跨 monitor)

piri sticky add --cross

取消 sticky

piri sticky delete

详细说明请参考 Sticky 文档

Window Order

https://github.com/user-attachments/assets/2c9cbbb4-7001-44ce-acfd-afb51dfbc372

https://github.com/user-attachments/assets/9818d478-3a33-456b-8367-548bb8ab7da7

按权重自动重排窗口,权重越大越靠左。

配置示例

[piri.plugins] window_order = true

[piri.window_order] enableeventlistener = true # 启用事件监听,自动重排 default_weight = 0 # 未配置窗口的默认权重

workspaces = ["1", "2", "dev"] # 可选:仅在指定工作区应用(空列表 = 所有工作区)

[window_order] google-chrome = 100 code = 80 ghostty = 70

快速使用

# 手动触发窗口重排(可在任意工作区执行) piri window_order toggle

特性

  • 智能排序,最小化移动次数
  • 支持手动/事件驱动触发
  • 支持工作区过滤
  • 相同权重保持相对顺序
  • 支持 app_id 部分匹配
详细说明请参考 Window Order 文档

Swallow

https://github.com/user-attachments/assets/9968e97f-fdea-4211-a007-717edf703e93

https://github.com/user-attachments/assets/51567d89-8ca8-4f4a-b2ca-732dfc0741c9

子窗口打开时自动隐藏父窗口,让子窗口替换父窗口位置。适用于终端启动图片查看器或媒体播放器等场景。

配置示例

[piri.plugins] swallow = true

[piri.swallow] usepidmatching = true # 启用基于 PID 的父子进程匹配(默认:true)

全局排除规则(可选)

[piri.swallow.exclude] app_id = [".dialog."]

规则列表

[[swallow]] parentappid = [".terminal.", ".alacritty.", ".foot.", ".ghostty."] childappid = [".mpv.", ".imv.", ".feh."]

[[swallow]] parentappid = ["code", "nvim-qt"] childappid = [".preview.", ".markdown."]

特性

  • 支持 PID 父子进程匹配(默认启用)
  • 支持规则匹配(app_id/title/pid
  • 支持全局/规则级排除
  • 智能聚焦窗口队列
  • 自动处理工作区移动与浮动转换
详细说明请参考 Swallow 文档

文档

许可证

MIT License

参考项目

本项目受到 Pyprland 的启发。Pyprland 是一个为 Hyprland 合成器提供扩展功能的优秀项目,提供了大量插件来增强用户体验。

🔗 More in this category

© 2026 GitRepoTrend · Asthestarsfalll/piri · Updated daily from GitHub