<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hyprland on Tarragon</title><link>https://tarrragon.github.io/blog/tags/hyprland/</link><description>Recent content in Hyprland on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 02 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/hyprland/index.xml" rel="self" type="application/rss+xml"/><item><title>Linux 桌面的故障隔離模型</title><link>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/fault-isolation-model/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/fault-isolation-model/</guid><description>&lt;p>Linux 桌面環境的故障隔離建立在一個結構性的設計決策上：&lt;strong>顯示合成器（compositor）是 userspace process，不是 kernel 的一部分&lt;/strong>。這意味著 Hyprland 掛了只是桌面消失，作業系統核心還在正常運作。&lt;/p>
&lt;p>本文用「桌面環境」泛指使用者看到的圖形介面整體。技術上，Hyprland 是 Wayland compositor——負責視窗合成和輸入管理，不含完整桌面環境（DE）的套件管理、設定面板等元件。GNOME、KDE Plasma 才是完整的 DE。但在故障隔離的討論中，關鍵區分是 kernel space vs userspace，compositor 和 DE 都在 userspace 這一側。&lt;/p>
&lt;h2 id="kernel-與-userspace-的隔離邊界">Kernel 與 Userspace 的隔離邊界&lt;/h2>
&lt;p>作業系統分成兩個執行空間。Kernel space 負責硬體驅動、記憶體管理、process 排程這些基礎設施。Userspace 跑所有應用程式，包括桌面環境本身。&lt;/p>
&lt;p>兩個空間之間有硬體層級的隔離——CPU 的保護環機制（ring 0 是核心層級、ring 3 是應用程式層級，硬體強制限制 ring 3 的程式碼存取 ring 0 的記憶體）。Userspace 的 process 不管怎麼崩潰，都不會直接影響 kernel。Kernel 會清理掉崩潰的 process、回收它佔用的記憶體，然後繼續運作。&lt;/p>
&lt;p>這個隔離機制解釋了一個關鍵差異：為什麼 Linux 上一個 app crash 通常只是那個視窗消失，而不會拖垮整台機器。&lt;/p>
&lt;h2 id="為什麼-windows-會藍屏">為什麼 Windows 會藍屏&lt;/h2>
&lt;p>Windows 的藍屏（Blue Screen of Death, BSOD）是 kernel panic 的表現——作業系統核心本身遇到無法恢復的錯誤，只能停機。&lt;/p>
&lt;p>Windows 藍屏頻率較高的結構性原因在於&lt;strong>顯示驅動的執行位置&lt;/strong>。Windows 把 GPU 驅動放在 kernel mode（WDDM 架構），NVIDIA 或 AMD 的驅動程式碼直接跑在核心空間。驅動有 bug 時，錯誤發生在 kernel space，清理掉再繼續的選項不存在——繼續執行可能造成資料損壞，只能停機。&lt;/p>
&lt;p>藍屏頻率高是架構選擇的代價。把驅動放在 kernel mode 可以減少 context switch 的效能開銷，GPU 效能更好。代價是驅動 bug 的爆炸半徑從「app crash」升級成「整台停機」。Windows 10/11 已加入 TDR（Timeout Detection and Recovery）機制——GPU driver hang 時系統嘗試 reset driver 而非直接藍屏，大幅降低了 GPU 導致的 BSOD 頻率。但架構上 driver 仍在 kernel mode，藍屏的可能性仍然存在。&lt;/p>
&lt;h2 id="linux-桌面的架構差異">Linux 桌面的架構差異&lt;/h2>
&lt;p>Linux 桌面環境的顯示合成器（Hyprland、Sway、KDE Plasma 的 KWin）跑在 userspace。它們透過 DRM/KMS（Direct Rendering Manager / Kernel Mode Setting，Linux 的顯示子系統介面）跟 kernel 的 GPU 驅動溝通，但合成器本身的程式碼不在 kernel space 裡。&lt;/p>
&lt;p>這個架構選擇的效果：&lt;/p>
&lt;p>&lt;strong>Compositor crash&lt;/strong>。Hyprland 如果遇到 segfault 或其他 fatal error，kernel 終止這個 userspace process。所有由它管理的視窗消失，螢幕回到 TTY 登入畫面或黑屏。但 kernel 還在跑——其他 TTY 可以登入，SSH 可以連線，背景的 service 繼續運作。&lt;/p>
&lt;p>&lt;strong>GPU driver bug&lt;/strong>。Linux 的 GPU 驅動分兩層：kernel module（可動態載入的核心擴充模組，如 &lt;code>nvidia.ko&lt;/code>、&lt;code>amdgpu.ko&lt;/code>）負責硬體操作，userspace 的 Mesa / NVIDIA userspace driver 負責 OpenGL/Vulkan 實作。Kernel module 出問題理論上可以 kernel panic，但實際行為取決於驅動。AMD 的開源 &lt;code>amdgpu&lt;/code> 通常會嘗試 reset GPU 而非直接 panic，常見的表現是畫面凍結幾秒後恢復。NVIDIA 的閉源 &lt;code>nvidia.ko&lt;/code> 是隔離模型的主要例外——kernel 社群無法審查或修復其程式碼，hang 時恢復能力遠弱於 amdgpu，經常拖垮整個 session 且 TTY 切換也受影響。這是後續&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">故障場景&lt;/a>中 NVIDIA 相關 caveat 的根源。&lt;/p></description><content:encoded><![CDATA[<p>Linux 桌面環境的故障隔離建立在一個結構性的設計決策上：<strong>顯示合成器（compositor）是 userspace process，不是 kernel 的一部分</strong>。這意味著 Hyprland 掛了只是桌面消失，作業系統核心還在正常運作。</p>
<p>本文用「桌面環境」泛指使用者看到的圖形介面整體。技術上，Hyprland 是 Wayland compositor——負責視窗合成和輸入管理，不含完整桌面環境（DE）的套件管理、設定面板等元件。GNOME、KDE Plasma 才是完整的 DE。但在故障隔離的討論中，關鍵區分是 kernel space vs userspace，compositor 和 DE 都在 userspace 這一側。</p>
<h2 id="kernel-與-userspace-的隔離邊界">Kernel 與 Userspace 的隔離邊界</h2>
<p>作業系統分成兩個執行空間。Kernel space 負責硬體驅動、記憶體管理、process 排程這些基礎設施。Userspace 跑所有應用程式，包括桌面環境本身。</p>
<p>兩個空間之間有硬體層級的隔離——CPU 的保護環機制（ring 0 是核心層級、ring 3 是應用程式層級，硬體強制限制 ring 3 的程式碼存取 ring 0 的記憶體）。Userspace 的 process 不管怎麼崩潰，都不會直接影響 kernel。Kernel 會清理掉崩潰的 process、回收它佔用的記憶體，然後繼續運作。</p>
<p>這個隔離機制解釋了一個關鍵差異：為什麼 Linux 上一個 app crash 通常只是那個視窗消失，而不會拖垮整台機器。</p>
<h2 id="為什麼-windows-會藍屏">為什麼 Windows 會藍屏</h2>
<p>Windows 的藍屏（Blue Screen of Death, BSOD）是 kernel panic 的表現——作業系統核心本身遇到無法恢復的錯誤，只能停機。</p>
<p>Windows 藍屏頻率較高的結構性原因在於<strong>顯示驅動的執行位置</strong>。Windows 把 GPU 驅動放在 kernel mode（WDDM 架構），NVIDIA 或 AMD 的驅動程式碼直接跑在核心空間。驅動有 bug 時，錯誤發生在 kernel space，清理掉再繼續的選項不存在——繼續執行可能造成資料損壞，只能停機。</p>
<p>藍屏頻率高是架構選擇的代價。把驅動放在 kernel mode 可以減少 context switch 的效能開銷，GPU 效能更好。代價是驅動 bug 的爆炸半徑從「app crash」升級成「整台停機」。Windows 10/11 已加入 TDR（Timeout Detection and Recovery）機制——GPU driver hang 時系統嘗試 reset driver 而非直接藍屏，大幅降低了 GPU 導致的 BSOD 頻率。但架構上 driver 仍在 kernel mode，藍屏的可能性仍然存在。</p>
<h2 id="linux-桌面的架構差異">Linux 桌面的架構差異</h2>
<p>Linux 桌面環境的顯示合成器（Hyprland、Sway、KDE Plasma 的 KWin）跑在 userspace。它們透過 DRM/KMS（Direct Rendering Manager / Kernel Mode Setting，Linux 的顯示子系統介面）跟 kernel 的 GPU 驅動溝通，但合成器本身的程式碼不在 kernel space 裡。</p>
<p>這個架構選擇的效果：</p>
<p><strong>Compositor crash</strong>。Hyprland 如果遇到 segfault 或其他 fatal error，kernel 終止這個 userspace process。所有由它管理的視窗消失，螢幕回到 TTY 登入畫面或黑屏。但 kernel 還在跑——其他 TTY 可以登入，SSH 可以連線，背景的 service 繼續運作。</p>
<p><strong>GPU driver bug</strong>。Linux 的 GPU 驅動分兩層：kernel module（可動態載入的核心擴充模組，如 <code>nvidia.ko</code>、<code>amdgpu.ko</code>）負責硬體操作，userspace 的 Mesa / NVIDIA userspace driver 負責 OpenGL/Vulkan 實作。Kernel module 出問題理論上可以 kernel panic，但實際行為取決於驅動。AMD 的開源 <code>amdgpu</code> 通常會嘗試 reset GPU 而非直接 panic，常見的表現是畫面凍結幾秒後恢復。NVIDIA 的閉源 <code>nvidia.ko</code> 是隔離模型的主要例外——kernel 社群無法審查或修復其程式碼，hang 時恢復能力遠弱於 amdgpu，經常拖垮整個 session 且 TTY 切換也受影響。這是後續<a href="/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">故障場景</a>中 NVIDIA 相關 caveat 的根源。</p>
<p><strong>應用程式 crash</strong>。Firefox、VS Code、任何 GUI 程式崩潰，只有那個視窗消失。Compositor 繼續管理剩下的視窗，桌面環境不受影響。</p>
<h2 id="ttykernel-存活時的首選救生通道">TTY：kernel 存活時的首選救生通道</h2>
<p>TTY（TeleTYpewriter）是 Linux 核心直接提供的純文字終端機介面，獨立於任何桌面環境。systemd 預設配置下有 6 個 virtual console（TTY1-TTY6）。Wayland compositor（如 Hyprland）通常佔用 TTY1，其餘可用。</p>
<p>切換方式：<code>Ctrl+Alt+F2</code>（切到 TTY2）到 <code>Ctrl+Alt+F6</code>（切到 TTY6）。</p>
<p>TTY 的重要性在於它<strong>不依賴 compositor</strong>。Hyprland 掛了、compositor crash 導致桌面消失——只要 kernel 還活著、GPU driver 仍能處理 VT switch，TTY 就能切過去登入操作：</p>
<ul>
<li>用 <code>htop</code> 或 <code>ps</code> 查看哪個 process 出問題</li>
<li><code>kill</code> 有問題的 process</li>
<li>用 <code>vim</code> 或 <code>nano</code> 修改配置檔</li>
<li>重新啟動 Hyprland（<code>Hyprland</code> 指令）</li>
<li>如果需要，正常 <code>reboot</code></li>
</ul>
<p>TTY 切換失效的情境有兩種：kernel panic（極罕見）和 GPU 完全 hang 導致 VT switch 本身卡住（NVIDIA 閉源驅動在 Wayland 上較常見，需確保 <code>nvidia_drm.modeset=1</code>）。後者的替代手段是 SSH 遠端登入或 Magic SysRq 鍵（見<a href="/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景</a>的場景三）。</p>
<h2 id="記憶體耗盡oom的處理機制">記憶體耗盡（OOM）的處理機制</h2>
<p>Linux kernel 有 OOM Killer（Out of Memory Killer）機制——當記憶體和 swap 都用完、kernel 無法再分配新頁面時，自動挑選佔用記憶體最多、重要性最低的 process 強制終止，釋放記憶體讓系統繼續運作。</p>
<p>OOM Killer 的行為有時超出使用者的預期——它可能直接終止 Hyprland（因為 compositor 通常佔用不少記憶體），導致桌面突然消失。但關鍵是：<strong>系統沒有崩潰</strong>。Kernel 還在、TTY 還在、SSH 還在。</p>
<p>預防 OOM 的常見做法：</p>
<ul>
<li>設定 swap（即使用 SSD，2-4GB 的 swap 也能在記憶體壓力大時提供緩衝）</li>
<li>啟用 <code>systemd-oomd</code>（userspace 的 OOM 管理，比 kernel OOM Killer 更早介入、更可控）</li>
<li>監控記憶體用量（<code>btop</code> 或 <code>htop</code> 可以看即時狀態）</li>
</ul>
<h2 id="故障層級速查">故障層級速查</h2>
<table>
  <thead>
      <tr>
          <th>故障層級</th>
          <th>症狀</th>
          <th>系統影響</th>
          <th>恢復手段</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>應用程式 crash</td>
          <td>單一視窗消失</td>
          <td>無</td>
          <td>重開該程式</td>
      </tr>
      <tr>
          <td>工具 crash（waybar 等）</td>
          <td>狀態列 / 通知 / 啟動器消失</td>
          <td>無</td>
          <td>重啟該工具</td>
      </tr>
      <tr>
          <td>Compositor crash</td>
          <td>所有視窗消失、黑屏</td>
          <td>桌面環境不可用</td>
          <td>TTY 登入、重啟 compositor</td>
      </tr>
      <tr>
          <td>GPU driver hang</td>
          <td>畫面凍結</td>
          <td>桌面環境不可用</td>
          <td>TTY 或 SSH、kill compositor</td>
      </tr>
      <tr>
          <td>OOM</td>
          <td>系統極慢或桌面被殺</td>
          <td>部分 process 被終止</td>
          <td>TTY 登入、清理 process</td>
      </tr>
      <tr>
          <td>Kernel panic</td>
          <td>完全停機</td>
          <td>全機不可用</td>
          <td>只能重開機</td>
      </tr>
  </tbody>
</table>
<p>前五個層級都有恢復手段，只有 kernel panic 需要重開機。日常使用中遇到的故障多數落在前三層。</p>
]]></content:encoded></item><item><title>Hyprland 安裝與環境建置</title><link>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-installation/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-installation/</guid><description>&lt;p>Hyprland 的安裝分三層：compositor（負責視窗管理與畫面合成的核心程式，詳見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/wayland-explainer/" data-link-title="Wayland 顯示協議：為什麼 Hyprland 不跑在 X11 上" data-link-desc="想理解 Hyprland 底層的圖形架構、Wayland 跟 X11 的差異、XWayland 相容層、以及 2026 年 Wayland 已經是主流這件事時回來讀">Wayland 顯示協議&lt;/a>）本身、GPU 驅動、桌面配套工具。Compositor 只管視窗排列和畫面輸出，其餘功能（status bar、launcher、通知、音訊、藍牙、網路）都需要另外裝。本篇以 Arch Linux 為主要說明對象，其他發行版在最後簡述。&lt;/p>
&lt;h2 id="核心套件">核心套件&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">sudo pacman -S hyprland xorg-xwayland xdg-desktop-portal-hyprland&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>套件&lt;/th>
 &lt;th>角色&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>hyprland&lt;/code>&lt;/td>
 &lt;td>Compositor 本體，在 Arch 官方 &lt;code>extra&lt;/code> repo，不需要 AUR&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>xorg-xwayland&lt;/code>&lt;/td>
 &lt;td>X11 相容層，讓舊 X11 應用程式在 Wayland 環境裡正常運行&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>xdg-desktop-portal-hyprland&lt;/code>&lt;/td>
 &lt;td>Hyprland 專屬的 portal backend，處理 screen sharing 和檔案對話框&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;blockquote>
&lt;p>&lt;strong>[VM 可測試]&lt;/strong> 套件安裝本身在 VM 和實機上完全相同。&lt;/p>&lt;/blockquote>
&lt;h2 id="gpu-驅動">GPU 驅動&lt;/h2>
&lt;p>GPU 驅動決定 Hyprland 的渲染是走硬體加速還是軟體 fallback。AMD 和 Intel 開箱即用，NVIDIA 需要額外配置。&lt;/p>
&lt;h3 id="amd推薦開箱即用">AMD（推薦，開箱即用）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">sudo pacman -S mesa vulkan-radeon libva-mesa-driver mesa-vdpau&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>mkinitcpio MODULES 加入：&lt;code>amdgpu&lt;/code>&lt;/p>
&lt;p>環境變數（放在 Hyprland 配置裡）：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;LIBVA_DRIVER_NAME, radeonsi&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;VDPAU_DRIVER, radeonsi&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;AMD_VULKAN_ICD, RADV&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>AMD 是 Hyprland / Wayland 生態支援最好的 GPU。如果是新購硬體、有選擇的餘地，AMD 是最省事的選項。&lt;/p>
&lt;h3 id="intel開箱即用">Intel（開箱即用）&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">sudo pacman -S mesa vulkan-intel intel-media-driver&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>mkinitcpio MODULES 加入：&lt;code>i915&lt;/code>&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">env&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;LIBVA_DRIVER_NAME, iHD&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;VDPAU_DRIVER, va_gl&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Intel 內顯在 Wayland 上表現穩定，適合筆電和輕度桌面使用。&lt;/p>
&lt;h3 id="nvidia需要額外配置非官方支援">NVIDIA（需要額外配置，非官方支援）&lt;/h3>
&lt;blockquote>
&lt;p>&lt;strong>[需實機測試]&lt;/strong> NVIDIA 的所有設定在 VM 中無意義——VM 使用 virtio-gpu 或軟體渲染，不走 NVIDIA 驅動。以下步驟只在實體機有 NVIDIA 顯卡時才需要。&lt;/p>&lt;/blockquote>
&lt;p>Hyprland 官方不支援 NVIDIA，但社群有成熟的 workaround。最低版本需求：&lt;/p>
&lt;ul>
&lt;li>NVIDIA driver &amp;gt;= 555（555 引入完整 GBM 支援和 explicit sync，是 Wayland 硬體渲染的最低依賴。535 系列有 explicit sync 問題，Wayland 下容易 flicker。590+ 修復多數 HDR 和多螢幕邊界問題）&lt;/li>
&lt;li>xorg-xwayland &amp;gt;= 24.1&lt;/li>
&lt;li>wayland-protocols &amp;gt;= 1.34&lt;/li>
&lt;/ul>
&lt;p>安裝：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">sudo pacman -S nvidia-dkms nvidia-utils libva-nvidia-driver
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># 32-bit 支援（遊戲可能需要）：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">sudo pacman -S lib32-nvidia-utils&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>mkinitcpio MODULES（依硬體配置選擇）：&lt;/p>
&lt;p>純 NVIDIA 桌機：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Intel + NVIDIA hybrid 筆電（Intel 排在 NVIDIA 前面）：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">MODULES=(i915 nvidia nvidia_modeset nvidia_uvm nvidia_drm)&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>更新 initramfs：&lt;/p></description><content:encoded><![CDATA[<p>Hyprland 的安裝分三層：compositor（負責視窗管理與畫面合成的核心程式，詳見 <a href="/blog/linux/dotfile/04-window-management/wayland-explainer/" data-link-title="Wayland 顯示協議：為什麼 Hyprland 不跑在 X11 上" data-link-desc="想理解 Hyprland 底層的圖形架構、Wayland 跟 X11 的差異、XWayland 相容層、以及 2026 年 Wayland 已經是主流這件事時回來讀">Wayland 顯示協議</a>）本身、GPU 驅動、桌面配套工具。Compositor 只管視窗排列和畫面輸出，其餘功能（status bar、launcher、通知、音訊、藍牙、網路）都需要另外裝。本篇以 Arch Linux 為主要說明對象，其他發行版在最後簡述。</p>
<h2 id="核心套件">核心套件</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S hyprland xorg-xwayland xdg-desktop-portal-hyprland</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>套件</th>
          <th>角色</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>hyprland</code></td>
          <td>Compositor 本體，在 Arch 官方 <code>extra</code> repo，不需要 AUR</td>
      </tr>
      <tr>
          <td><code>xorg-xwayland</code></td>
          <td>X11 相容層，讓舊 X11 應用程式在 Wayland 環境裡正常運行</td>
      </tr>
      <tr>
          <td><code>xdg-desktop-portal-hyprland</code></td>
          <td>Hyprland 專屬的 portal backend，處理 screen sharing 和檔案對話框</td>
      </tr>
  </tbody>
</table>
<blockquote>
<p><strong>[VM 可測試]</strong> 套件安裝本身在 VM 和實機上完全相同。</p></blockquote>
<h2 id="gpu-驅動">GPU 驅動</h2>
<p>GPU 驅動決定 Hyprland 的渲染是走硬體加速還是軟體 fallback。AMD 和 Intel 開箱即用，NVIDIA 需要額外配置。</p>
<h3 id="amd推薦開箱即用">AMD（推薦，開箱即用）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S mesa vulkan-radeon libva-mesa-driver mesa-vdpau</span></span></code></pre></div><p>mkinitcpio MODULES 加入：<code>amdgpu</code></p>
<p>環境變數（放在 Hyprland 配置裡）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="s2">&#34;LIBVA_DRIVER_NAME, radeonsi&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s2">&#34;VDPAU_DRIVER, radeonsi&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s2">&#34;AMD_VULKAN_ICD, RADV&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>AMD 是 Hyprland / Wayland 生態支援最好的 GPU。如果是新購硬體、有選擇的餘地，AMD 是最省事的選項。</p>
<h3 id="intel開箱即用">Intel（開箱即用）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S mesa vulkan-intel intel-media-driver</span></span></code></pre></div><p>mkinitcpio MODULES 加入：<code>i915</code></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="s2">&#34;LIBVA_DRIVER_NAME, iHD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s2">&#34;VDPAU_DRIVER, va_gl&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Intel 內顯在 Wayland 上表現穩定，適合筆電和輕度桌面使用。</p>
<h3 id="nvidia需要額外配置非官方支援">NVIDIA（需要額外配置，非官方支援）</h3>
<blockquote>
<p><strong>[需實機測試]</strong> NVIDIA 的所有設定在 VM 中無意義——VM 使用 virtio-gpu 或軟體渲染，不走 NVIDIA 驅動。以下步驟只在實體機有 NVIDIA 顯卡時才需要。</p></blockquote>
<p>Hyprland 官方不支援 NVIDIA，但社群有成熟的 workaround。最低版本需求：</p>
<ul>
<li>NVIDIA driver &gt;= 555（555 引入完整 GBM 支援和 explicit sync，是 Wayland 硬體渲染的最低依賴。535 系列有 explicit sync 問題，Wayland 下容易 flicker。590+ 修復多數 HDR 和多螢幕邊界問題）</li>
<li>xorg-xwayland &gt;= 24.1</li>
<li>wayland-protocols &gt;= 1.34</li>
</ul>
<p>安裝：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S nvidia-dkms nvidia-utils libva-nvidia-driver
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 32-bit 支援（遊戲可能需要）：</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">sudo pacman -S lib32-nvidia-utils</span></span></code></pre></div><p>mkinitcpio MODULES（依硬體配置選擇）：</p>
<p>純 NVIDIA 桌機：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">MODULES=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)</span></span></code></pre></div><p>Intel + NVIDIA hybrid 筆電（Intel 排在 NVIDIA 前面）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">MODULES=(i915 nvidia nvidia_modeset nvidia_uvm nvidia_drm)</span></span></code></pre></div><p>更新 initramfs：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo mkinitcpio -P</span></span></code></pre></div><p>Modprobe 設定：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl"># /etc/modprobe.d/nvidia.conf
</span></span><span class="line"><span class="ln">2</span><span class="cl">options nvidia_drm modeset=1</span></span></code></pre></div><p>GRUB kernel parameter：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl"># /etc/default/grub
</span></span><span class="line"><span class="ln">2</span><span class="cl">GRUB_CMDLINE_LINUX_DEFAULT=&#34;... nvidia_drm.modeset=1 nvidia_drm.fbdev=1&#34;</span></span></code></pre></div><p>更新 GRUB：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo grub-mkconfig -o /boot/grub/grub.cfg</span></span></code></pre></div><p>Hyprland 配置裡的 NVIDIA 環境變數：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="s2">&#34;LIBVA_DRIVER_NAME, nvidia&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s2">&#34;__GLX_VENDOR_LIBRARY_NAME, nvidia&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s2">&#34;GBM_BACKEND, nvidia-drm&#34;</span><span class="p">,</span>         <span class="c1">-- 如果 Firefox crash，移除這行</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="s2">&#34;NVD_BACKEND, direct&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Cursor 修正（NVIDIA 常見的硬體 cursor 問題）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">cursor</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">no_hardware_cursors</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="n">allow_dumb_copy</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p>Suspend/resume 支援：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl <span class="nb">enable</span> nvidia-suspend.service nvidia-hibernate.service nvidia-resume.service</span></span></code></pre></div><h3 id="檢查-gpu-驅動狀態">檢查 GPU 驅動狀態</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">lspci -k <span class="p">|</span> grep -A <span class="m">3</span> VGA        <span class="c1"># 查看使用中的驅動</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">glxinfo <span class="p">|</span> grep <span class="s2">&#34;OpenGL renderer&#34;</span> <span class="c1"># 查看活躍的 renderer</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">vulkaninfo --summary             <span class="c1"># Vulkan 裝置資訊</span></span></span></code></pre></div><p>GPU 驅動相關的故障診斷（driver hang、畫面凍結、suspend/resume 後異常）見<a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">桌面環境維護與故障排除</a>。</p>
<h2 id="桌面配套套件">桌面配套套件</h2>
<p>Hyprland 只管視窗，以下是一個可用桌面需要的配套工具：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">sudo pacman -S <span class="se">\
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="se"></span>  kitty waybar wofi mako <span class="se">\
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="se"></span>  polkit-gnome grim slurp wl-clipboard cliphist <span class="se">\
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="se"></span>  hyprpaper hyprlock hypridle <span class="se">\
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="se"></span>  pipewire pipewire-alsa pipewire-jack pipewire-pulse wireplumber <span class="se">\
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="se"></span>  pamixer pavucontrol brightnessctl <span class="se">\
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="se"></span>  bluez bluez-utils blueman <span class="se">\
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="se"></span>  networkmanager network-manager-applet <span class="se">\
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="se"></span>  thunar gvfs <span class="se">\
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="se"></span>  qt5-wayland qt6-wayland <span class="se">\
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="se"></span>  ttf-meslo-nerd noto-fonts noto-fonts-cjk</span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>類別</th>
          <th>套件</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>終端機</td>
          <td><code>kitty</code></td>
          <td>Hyprland 預設配置使用的 terminal emulator</td>
      </tr>
      <tr>
          <td>狀態列</td>
          <td><code>waybar</code></td>
          <td>JSON config + CSS styling 的 status bar</td>
      </tr>
      <tr>
          <td>啟動器</td>
          <td><code>wofi</code></td>
          <td>Wayland 原生 app launcher</td>
      </tr>
      <tr>
          <td>通知</td>
          <td><code>mako</code></td>
          <td>輕量 notification daemon</td>
      </tr>
      <tr>
          <td>認證代理</td>
          <td><code>polkit-gnome</code></td>
          <td>GUI 權限提升（sudo 對話框）</td>
      </tr>
      <tr>
          <td>截圖</td>
          <td><code>grim</code> + <code>slurp</code></td>
          <td>截圖工具 + 選區工具</td>
      </tr>
      <tr>
          <td>剪貼簿</td>
          <td><code>wl-clipboard</code> + <code>cliphist</code></td>
          <td>剪貼簿存取 + 歷史記錄</td>
      </tr>
      <tr>
          <td>桌布</td>
          <td><code>hyprpaper</code></td>
          <td>靜態桌布</td>
      </tr>
      <tr>
          <td>鎖屏</td>
          <td><code>hyprlock</code></td>
          <td>Hyprland 配套鎖屏</td>
      </tr>
      <tr>
          <td>Idle</td>
          <td><code>hypridle</code></td>
          <td>閒置偵測（觸發鎖屏/休眠）</td>
      </tr>
      <tr>
          <td>音訊</td>
          <td><code>pipewire</code> + <code>wireplumber</code> + <code>pipewire-pulse</code></td>
          <td>音訊 server（取代 PulseAudio）</td>
      </tr>
      <tr>
          <td>音量控制</td>
          <td><code>pamixer</code>（CLI）+ <code>pavucontrol</code>（GUI）</td>
          <td>音量調整</td>
      </tr>
      <tr>
          <td>亮度</td>
          <td><code>brightnessctl</code></td>
          <td>螢幕亮度（筆電）</td>
      </tr>
      <tr>
          <td>藍牙</td>
          <td><code>bluez</code> + <code>bluez-utils</code> + <code>blueman</code></td>
          <td>藍牙協定 + GUI 管理</td>
      </tr>
      <tr>
          <td>網路</td>
          <td><code>networkmanager</code> + <code>nm-applet</code></td>
          <td>網路管理 + system tray 圖示</td>
      </tr>
      <tr>
          <td>檔案管理</td>
          <td><code>thunar</code> + <code>gvfs</code></td>
          <td>圖形檔案管理器 + 回收筒/遠端掛載</td>
      </tr>
      <tr>
          <td>Qt 相容</td>
          <td><code>qt5-wayland</code> + <code>qt6-wayland</code></td>
          <td>Qt 應用在 Wayland 下正常運行</td>
      </tr>
      <tr>
          <td>字型</td>
          <td><code>ttf-meslo-nerd</code> + <code>noto-fonts-cjk</code></td>
          <td>Nerd Font（waybar icon 需要）+ CJK 字型</td>
      </tr>
  </tbody>
</table>
<p>AUR 補充套件（用 <code>yay</code> 或 <code>paru</code>）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">yay -S grimblast-git   <span class="c1"># Hyprland 截圖 wrapper</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">yay -S swww            <span class="c1"># 動態桌布（hyprpaper 的替代）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">yay -S rofi-wayland    <span class="c1"># 功能比 wofi 更豐富的 launcher</span></span></span></code></pre></div><blockquote>
<p><strong>[已驗證]</strong> 套件安裝在 VM 中完全可執行。waybar、wofi、mako、hyprlock 在 virtio-gpu-gl-pci 加速下正常 render（Hyprland 0.55 + UTM 4.7.5 實測）。</p>
<p><strong>[需實機測試]</strong> 藍牙（<code>bluez</code>）、亮度（<code>brightnessctl</code>）、音訊裝置偵測（<code>pipewire</code>）需要實際硬體。</p></blockquote>
<h2 id="登入管理器">登入管理器</h2>
<p>Hyprland 預設不含登入管理器。三個選項：</p>
<h3 id="從-tty-直接啟動最簡單">從 TTY 直接啟動（最簡單）</h3>
<p>登入文字介面後直接執行 <code>Hyprland</code>。建議用 wrapper script 設定必要的環境變數：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="cp">#!/bin/sh
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="cp"></span><span class="c1"># ~/start-hyprland.sh</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">export</span> <span class="nv">XDG_SESSION_TYPE</span><span class="o">=</span>wayland
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nb">export</span> <span class="nv">XDG_CURRENT_DESKTOP</span><span class="o">=</span>Hyprland
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nb">export</span> <span class="nv">XDG_SESSION_DESKTOP</span><span class="o">=</span>Hyprland
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nb">exec</span> Hyprland</span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">chmod +x ~/start-hyprland.sh
</span></span><span class="line"><span class="ln">2</span><span class="cl">~/start-hyprland.sh</span></span></code></pre></div><h3 id="greetd--tuigreet推薦">greetd + tuigreet（推薦）</h3>
<p>greetd 是一個輕量的登入管理器，tuigreet 是它的 TUI 前端：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S greetd greetd-tuigreet</span></span></code></pre></div><p>設定 <code>/etc/greetd/config.toml</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">terminal</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">vt</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">[</span><span class="nx">default_session</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">command</span> <span class="p">=</span> <span class="s2">&#34;tuigreet --cmd Hyprland&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">user</span> <span class="p">=</span> <span class="s2">&#34;greeter&#34;</span></span></span></code></pre></div><p>自動登入（可選）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">initial_session</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">command</span> <span class="p">=</span> <span class="s2">&#34;Hyprland&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">user</span> <span class="p">=</span> <span class="s2">&#34;yourusername&#34;</span></span></span></code></pre></div><p>啟用服務：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl <span class="nb">enable</span> greetd</span></span></code></pre></div><h3 id="sddm">SDDM</h3>
<p>如果已經有 KDE 或偏好圖形化登入介面：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S sddm
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo systemctl <span class="nb">enable</span> sddm</span></span></code></pre></div><p>SDDM 會自動偵測到 Hyprland 並在登入畫面顯示它作為 session 選項。</p>
<blockquote>
<p><strong>[VM 可測試]</strong> 登入管理器的設定在 VM 中可完整測試。</p></blockquote>
<h2 id="首次啟動常見問題">首次啟動常見問題</h2>
<table>
  <thead>
      <tr>
          <th>症狀</th>
          <th>原因</th>
          <th>修正</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>黑屏、沒有 cursor</td>
          <td>缺少 polkit agent 或 seatd service</td>
          <td><code>sudo systemctl enable --now seatd</code> 或安裝 <code>polkit-gnome</code></td>
      </tr>
      <tr>
          <td>開不了 terminal</td>
          <td>預設 keybind 用的是 kitty，但 kitty 沒裝</td>
          <td><code>sudo pacman -S kitty</code> 或改 keybind 指向已安裝的 terminal</td>
      </tr>
      <tr>
          <td>Cursor 不見（NVIDIA）</td>
          <td>硬體 cursor 問題</td>
          <td>設定 <code>cursor { no_hardware_cursors = true }</code></td>
      </tr>
      <tr>
          <td>Portal 衝突，screen share 失敗</td>
          <td>同時裝了多個 portal backend</td>
          <td>移除 <code>xdg-desktop-portal-gnome</code> 和 <code>xdg-desktop-portal-gtk</code></td>
      </tr>
      <tr>
          <td>沒有音訊</td>
          <td>PipeWire 服務未啟動</td>
          <td><code>systemctl --user enable --now pipewire wireplumber</code></td>
      </tr>
      <tr>
          <td>Config 報錯但不影響使用</td>
          <td>自動產生的預設 config 語法不完整</td>
          <td>emergency keybind 仍可用：SUPER+Q 開 terminal、SUPER+M 離開</td>
      </tr>
  </tbody>
</table>
<h2 id="配置格式lua-取代-hyprlangv055">配置格式：Lua 取代 hyprlang（v0.55+）</h2>
<p>Hyprland v0.55（2026 年 4 月）起，配置格式從 hyprlang（<code>.conf</code>）遷移到 <strong>Lua</strong>（<code>.lua</code>）。舊的 <code>hyprland.conf</code> 在沒有 <code>.lua</code> 檔案時仍然可用，但 hyprlang 已被標記為 deprecated，預計在後續版本移除。</p>
<table>
  <thead>
      <tr>
          <th>格式</th>
          <th>檔案</th>
          <th>狀態</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Lua（新）</td>
          <td><code>~/.config/hypr/hyprland.lua</code></td>
          <td>推薦</td>
      </tr>
      <tr>
          <td>hyprlang（舊）</td>
          <td><code>~/.config/hypr/hyprland.conf</code></td>
          <td>deprecated</td>
      </tr>
  </tbody>
</table>
<p>遷移工具：<code>hyprlang2lua</code>（Go，有瀏覽器版）、<code>hyprconf2lua</code>（Python pip）、<code>hypr-migrate</code>。</p>
<p>本系列後續文章（<a href="/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/" data-link-title="Hyprland 核心配置" data-link-desc="Hyprland 的配置檔該怎麼組織、monitor 怎麼設定、keybind 怎麼設計、輸入裝置和環境變數怎麼配時回來讀">Hyprland 核心配置</a>、<a href="/blog/linux/dotfile/05-hyprland-config/workspace-rules-appearance/" data-link-title="Workspace、Window Rules 與外觀" data-link-desc="Hyprland 的 workspace 綁定螢幕、window rules 設定浮動例外、外觀動畫調教、layout 選型、autostart 和 plugin 管理時回來讀">Workspace 與外觀</a>、<a href="/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&#43;wofi&#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia</a>）都使用 Lua 格式。如果在網路上看到 <code>.conf</code> 格式的教學，多數仍然可用，但建議儘早遷移到 Lua。</p>
<p>拆分配置用 Lua 原生的 <code>require()</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">-- hyprland.lua</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;keybinds&#34;</span><span class="p">)</span>    <span class="c1">-- 載入同目錄下的 keybinds.lua</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;rules&#34;</span><span class="p">)</span>       <span class="c1">-- 載入同目錄下的 rules.lua</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;appearance&#34;</span><span class="p">)</span></span></span></code></pre></div><p>詳細的配置語法和設定邏輯在<a href="/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/" data-link-title="Hyprland 核心配置" data-link-desc="Hyprland 的配置檔該怎麼組織、monitor 怎麼設定、keybind 怎麼設計、輸入裝置和環境變數怎麼配時回來讀">核心配置</a>和 <a href="/blog/linux/dotfile/05-hyprland-config/workspace-rules-appearance/" data-link-title="Workspace、Window Rules 與外觀" data-link-desc="Hyprland 的 workspace 綁定螢幕、window rules 設定浮動例外、外觀動畫調教、layout 選型、autostart 和 plugin 管理時回來讀">Workspace、Window Rules 與外觀</a>。</p>
<h2 id="其他發行版">其他發行版</h2>
<h3 id="fedora">Fedora</h3>
<p>Fedora 39 起即在官方 repo。安裝：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo dnf install hyprland</span></span></code></pre></div><p>JaKooLit 提供自動化安裝腳本（<code>github.com/JaKooLit/Fedora-Hyprland</code>），Fedora 42+ 運作良好。</p>
<h3 id="nixos">NixOS</h3>
<p>NixOS 有官方 module，在 <code>configuration.nix</code> 加入：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-nix" data-lang="nix"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">programs</span><span class="o">.</span><span class="n">hyprland</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span></span></span></code></pre></div><p>Home Manager 也有對應 module，可以宣告式管理 Hyprland 配置。</p>
<h3 id="ubuntu">Ubuntu</h3>
<p>不推薦。Ubuntu 的 point-release 模型跟 Hyprland 的 bleeding-edge 更新節奏衝突。沒有官方 PPA，從 source 編譯可行但維護成本高。如果一定要用 Debian 系，Arch 的 rolling release 或 Fedora 的半年週期更適合 Hyprland。</p>
<h2 id="安裝後的下一步">安裝後的下一步</h2>
<p>安裝完成後，VM 環境的額外設定（環境變數、效能調整、測試矩陣）見 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/" data-link-title="Hyprland VM 環境設定與測試矩陣" data-link-desc="要在 VM 裡測試 Hyprland 配置、或判斷某個設定該在 VM 還是實機驗證時回來讀">VM 環境設定與測試矩陣</a>。配置檔的組織方式和 keybind 設計見 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/" data-link-title="Hyprland 核心配置" data-link-desc="Hyprland 的配置檔該怎麼組織、monitor 怎麼設定、keybind 怎麼設計、輸入裝置和環境變數怎麼配時回來讀">Hyprland 核心配置</a>。</p>
]]></content:encoded></item><item><title>桌面 Shell 元件：狀態列、啟動器與通知</title><link>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/desktop-shell-components/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/desktop-shell-components/</guid><description>&lt;p>完整桌面環境（GNOME、KDE）把這些元件整合在一起出貨。平鋪式 WM 的桌面是拼裝的——每個位置自己選工具：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>功能&lt;/th>
 &lt;th>常見工具&lt;/th>
 &lt;th>配置格式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>狀態列&lt;/td>
 &lt;td>Waybar, Eww, AGS&lt;/td>
 &lt;td>JSON/JSONC, Yuck, JS&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>啟動器&lt;/td>
 &lt;td>Wofi, Rofi (wayland), Fuzzel&lt;/td>
 &lt;td>CSS + 設定檔&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>通知&lt;/td>
 &lt;td>Mako, Dunst, SwayNC&lt;/td>
 &lt;td>INI/TOML&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>鎖屏&lt;/td>
 &lt;td>Hyprlock, Swaylock&lt;/td>
 &lt;td>自定義格式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>桌布&lt;/td>
 &lt;td>Hyprpaper, Swww, Mpvpaper&lt;/td>
 &lt;td>自定義格式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>剪貼簿&lt;/td>
 &lt;td>wl-clipboard + Cliphist&lt;/td>
 &lt;td>CLI&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>螢幕截圖&lt;/td>
 &lt;td>Grimblast, Grim + Slurp&lt;/td>
 &lt;td>CLI&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Caelestia 這類「desktop shell 專案」做的就是把上述元件統一設計、統一配色、統一出貨，省去自己一個個挑的功夫。它用的是 Quickshell（QML 框架）把所有元件包成一套風格一致的桌面。本模組教的是自己組裝的方式——理解各元件的配置，之後要用 Caelestia 或自己拼都能做。&lt;/p>
&lt;h2 id="waybar狀態列">Waybar：狀態列&lt;/h2>
&lt;p>Waybar 是 Hyprland 桌面最常用的狀態列。配置在 &lt;code>~/.config/waybar/&lt;/code>，分兩個檔案：&lt;code>config.jsonc&lt;/code>（結構和模組）和 &lt;code>style.css&lt;/code>（外觀）。&lt;/p>
&lt;h3 id="結構配置">結構配置&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-jsonc" data-lang="jsonc">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">// ~/.config/waybar/config.jsonc
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;layer&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;top&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;position&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;top&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;height&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">36&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;spacing&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="c1">// 左中右三區塊各放哪些模組
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;modules-left&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;hyprland/workspaces&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;hyprland/window&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;modules-center&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;clock&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;modules-right&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;pulseaudio&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;network&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;cpu&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;memory&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;battery&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;tray&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="c1">// 各模組設定
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nt">&amp;#34;hyprland/workspaces&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{id}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;on-click&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;activate&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;clock&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{:%H:%M}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format-alt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{:%Y-%m-%d %H:%M}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;tooltip-format&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;tt&amp;gt;{calendar}&amp;lt;/tt&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;battery&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{capacity}% {icon}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format-icons&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;states&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;warning&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">30&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;critical&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">15&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;network&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format-wifi&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{essid} ({signalStrength}%)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format-ethernet&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{ifname}&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format-disconnected&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Disconnected&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">35&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">36&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;pulseaudio&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">37&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;{volume}%&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">38&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;on-click&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pavucontrol&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">39&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">40&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>format&lt;/code> 欄位裡的 icon 字元來自 Nerd Font——&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">終端機與編輯器&lt;/a>提到的字型安裝是這裡正常顯示的前提。&lt;/p>
&lt;h3 id="外觀-css">外觀 CSS&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-css" data-lang="css">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c">/* ~/.config/waybar/style.css */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="o">*&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="k">font-family&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;MesloLGS Nerd Font&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kc">monospace&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="k">font-size&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">13&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="nt">window&lt;/span>&lt;span class="p">#&lt;/span>&lt;span class="nn">waybar&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="k">background-color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">30&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">30&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">46&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.85&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#cdd6f4&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="p">#&lt;/span>&lt;span class="nn">workspaces&lt;/span> &lt;span class="nt">button&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#6c7086&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="k">border-radius&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">6&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">&lt;span class="p">#&lt;/span>&lt;span class="nn">workspaces&lt;/span> &lt;span class="nt">button&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">active&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#cdd6f4&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="k">background&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="nb">rgba&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">137&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">180&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">250&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="p">#&lt;/span>&lt;span class="nn">clock&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="p">#&lt;/span>&lt;span class="nn">battery&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="p">#&lt;/span>&lt;span class="nn">network&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="p">#&lt;/span>&lt;span class="nn">pulseaudio&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl"> &lt;span class="k">padding&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="kt">px&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="p">#&lt;/span>&lt;span class="nn">battery&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">warning&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f9e2af&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl">&lt;span class="p">#&lt;/span>&lt;span class="nn">battery&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nc">critical&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl"> &lt;span class="k">color&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mh">#f38ba8&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">34&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>CSS 裡的色碼（&lt;code>#cdd6f4&lt;/code>、&lt;code>#89b4fa&lt;/code>、&lt;code>#f38ba8&lt;/code>）來自配色方案（這個範例用的是 Catppuccin Mocha）。統一使用同一套色碼是 rice 視覺協調的基礎。&lt;/p>
&lt;p>Config 裡的字族名必須跟系統實際安裝的字族逐字相符。Nerd Font 的圖示落在專屬碼位範圍，只有對應的那支字帶這些 glyph——指定一個沒裝的字族名，文字排版引擎找不到 glyph 就會顯示豆腐方塊。確認實裝字族名的方式：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">fc-list &lt;span class="p">|&lt;/span> grep -i meslo
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># MesloLGSNerdFont-Regular.ttf: &amp;#34;MesloLGS Nerd Font&amp;#34;:style=Regular&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>引號內的字串是 config 該填的字族名。&lt;/p>
&lt;p>同一份 Waybar config 能同時服務筆電、桌機與 VM，靠的是模組對缺少硬體的自動退化：&lt;code>battery&lt;/code> 在沒有電池的機器直接隱藏該模組、不報錯也不留空位；&lt;code>pulseaudio&lt;/code> 在沒有音訊服務時顯示為空；&lt;code>network&lt;/code> 顯示當下實際在用的介面。不必為不同機器維護多份 config——把可能用到的模組都列上，用不到的那台自己消失。&lt;/p></description><content:encoded><![CDATA[<p>完整桌面環境（GNOME、KDE）把這些元件整合在一起出貨。平鋪式 WM 的桌面是拼裝的——每個位置自己選工具：</p>
<table>
  <thead>
      <tr>
          <th>功能</th>
          <th>常見工具</th>
          <th>配置格式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>狀態列</td>
          <td>Waybar, Eww, AGS</td>
          <td>JSON/JSONC, Yuck, JS</td>
      </tr>
      <tr>
          <td>啟動器</td>
          <td>Wofi, Rofi (wayland), Fuzzel</td>
          <td>CSS + 設定檔</td>
      </tr>
      <tr>
          <td>通知</td>
          <td>Mako, Dunst, SwayNC</td>
          <td>INI/TOML</td>
      </tr>
      <tr>
          <td>鎖屏</td>
          <td>Hyprlock, Swaylock</td>
          <td>自定義格式</td>
      </tr>
      <tr>
          <td>桌布</td>
          <td>Hyprpaper, Swww, Mpvpaper</td>
          <td>自定義格式</td>
      </tr>
      <tr>
          <td>剪貼簿</td>
          <td>wl-clipboard + Cliphist</td>
          <td>CLI</td>
      </tr>
      <tr>
          <td>螢幕截圖</td>
          <td>Grimblast, Grim + Slurp</td>
          <td>CLI</td>
      </tr>
  </tbody>
</table>
<p>Caelestia 這類「desktop shell 專案」做的就是把上述元件統一設計、統一配色、統一出貨，省去自己一個個挑的功夫。它用的是 Quickshell（QML 框架）把所有元件包成一套風格一致的桌面。本模組教的是自己組裝的方式——理解各元件的配置，之後要用 Caelestia 或自己拼都能做。</p>
<h2 id="waybar狀態列">Waybar：狀態列</h2>
<p>Waybar 是 Hyprland 桌面最常用的狀態列。配置在 <code>~/.config/waybar/</code>，分兩個檔案：<code>config.jsonc</code>（結構和模組）和 <code>style.css</code>（外觀）。</p>
<h3 id="結構配置">結構配置</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsonc" data-lang="jsonc"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// ~/.config/waybar/config.jsonc
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nt">&#34;layer&#34;</span><span class="p">:</span> <span class="s2">&#34;top&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;position&#34;</span><span class="p">:</span> <span class="s2">&#34;top&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt">&#34;height&#34;</span><span class="p">:</span> <span class="mi">36</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;spacing&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1">// 左中右三區塊各放哪些模組
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="nt">&#34;modules-left&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;hyprland/workspaces&#34;</span><span class="p">,</span> <span class="s2">&#34;hyprland/window&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="nt">&#34;modules-center&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;clock&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nt">&#34;modules-right&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;pulseaudio&#34;</span><span class="p">,</span> <span class="s2">&#34;network&#34;</span><span class="p">,</span> <span class="s2">&#34;cpu&#34;</span><span class="p">,</span> <span class="s2">&#34;memory&#34;</span><span class="p">,</span> <span class="s2">&#34;battery&#34;</span><span class="p">,</span> <span class="s2">&#34;tray&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1">// 各模組設定
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>    <span class="nt">&#34;hyprland/workspaces&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{id}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="nt">&#34;on-click&#34;</span><span class="p">:</span> <span class="s2">&#34;activate&#34;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nt">&#34;clock&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{:%H:%M}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="nt">&#34;format-alt&#34;</span><span class="p">:</span> <span class="s2">&#34;{:%Y-%m-%d %H:%M}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="nt">&#34;tooltip-format&#34;</span><span class="p">:</span> <span class="s2">&#34;&lt;tt&gt;{calendar}&lt;/tt&gt;&#34;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="nt">&#34;battery&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">        <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{capacity}% {icon}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="nt">&#34;format-icons&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="nt">&#34;states&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">            <span class="nt">&#34;warning&#34;</span><span class="p">:</span> <span class="mi">30</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">            <span class="nt">&#34;critical&#34;</span><span class="p">:</span> <span class="mi">15</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="nt">&#34;network&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="nt">&#34;format-wifi&#34;</span><span class="p">:</span> <span class="s2">&#34;{essid} ({signalStrength}%)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="nt">&#34;format-ethernet&#34;</span><span class="p">:</span> <span class="s2">&#34;{ifname}&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="nt">&#34;format-disconnected&#34;</span><span class="p">:</span> <span class="s2">&#34;Disconnected&#34;</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="nt">&#34;pulseaudio&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">        <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;{volume}%&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">        <span class="nt">&#34;on-click&#34;</span><span class="p">:</span> <span class="s2">&#34;pavucontrol&#34;</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><code>format</code> 欄位裡的 icon 字元來自 Nerd Font——<a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">終端機與編輯器</a>提到的字型安裝是這裡正常顯示的前提。</p>
<h3 id="外觀-css">外觀 CSS</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c">/* ~/.config/waybar/style.css */</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="o">*</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="s2">&#34;MesloLGS Nerd Font&#34;</span><span class="p">,</span> <span class="kc">monospace</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">font-size</span><span class="p">:</span> <span class="mi">13</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nt">window</span><span class="p">#</span><span class="nn">waybar</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">30</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">46</span><span class="p">,</span> <span class="mf">0.85</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#cdd6f4</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">#</span><span class="nn">workspaces</span> <span class="nt">button</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#6c7086</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">6</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">#</span><span class="nn">workspaces</span> <span class="nt">button</span><span class="p">.</span><span class="nc">active</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#cdd6f4</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="k">background</span><span class="p">:</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">137</span><span class="p">,</span> <span class="mi">180</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">#</span><span class="nn">clock</span><span class="o">,</span> <span class="p">#</span><span class="nn">battery</span><span class="o">,</span> <span class="p">#</span><span class="nn">network</span><span class="o">,</span> <span class="p">#</span><span class="nn">pulseaudio</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">0</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p">#</span><span class="nn">battery</span><span class="p">.</span><span class="nc">warning</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#f9e2af</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">#</span><span class="nn">battery</span><span class="p">.</span><span class="nc">critical</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#f38ba8</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>CSS 裡的色碼（<code>#cdd6f4</code>、<code>#89b4fa</code>、<code>#f38ba8</code>）來自配色方案（這個範例用的是 Catppuccin Mocha）。統一使用同一套色碼是 rice 視覺協調的基礎。</p>
<p>Config 裡的字族名必須跟系統實際安裝的字族逐字相符。Nerd Font 的圖示落在專屬碼位範圍，只有對應的那支字帶這些 glyph——指定一個沒裝的字族名，文字排版引擎找不到 glyph 就會顯示豆腐方塊。確認實裝字族名的方式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">fc-list <span class="p">|</span> grep -i meslo
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># MesloLGSNerdFont-Regular.ttf: &#34;MesloLGS Nerd Font&#34;:style=Regular</span></span></span></code></pre></div><p>引號內的字串是 config 該填的字族名。</p>
<p>同一份 Waybar config 能同時服務筆電、桌機與 VM，靠的是模組對缺少硬體的自動退化：<code>battery</code> 在沒有電池的機器直接隱藏該模組、不報錯也不留空位；<code>pulseaudio</code> 在沒有音訊服務時顯示為空；<code>network</code> 顯示當下實際在用的介面。不必為不同機器維護多份 config——把可能用到的模組都列上，用不到的那台自己消失。</p>
<h2 id="wofi--rofi啟動器">Wofi / Rofi：啟動器</h2>
<p>啟動器是按快捷鍵彈出的搜尋框，用來啟動應用程式、執行指令、搜尋檔案。</p>
<p>Wofi（Wayland 原生）配置：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># ~/.config/wofi/config</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="na">show</span><span class="o">=</span><span class="s">drun</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="na">width</span><span class="o">=</span><span class="s">600</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="na">height</span><span class="o">=</span><span class="s">400</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="na">prompt</span><span class="o">=</span><span class="s">Search...</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="na">insensitive</span><span class="o">=</span><span class="s">true</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="na">allow_markup</span><span class="o">=</span><span class="s">true</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c">/* ~/.config/wofi/style.css */</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">window</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#1e1e2e</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">border</span><span class="p">:</span> <span class="mi">2</span><span class="kt">px</span> <span class="kc">solid</span> <span class="mh">#89b4fa</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">#</span><span class="nn">input</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#313244</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#cdd6f4</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span> <span class="mi">12</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">#</span><span class="nn">entry</span><span class="p">:</span><span class="nd">selected</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="nb">rgba</span><span class="p">(</span><span class="mi">137</span><span class="p">,</span> <span class="mi">180</span><span class="p">,</span> <span class="mi">250</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Rofi（需要 wayland fork rofi-lbonn-wayland）功能更豐富——支援多種 mode（drun、window、ssh、自定義 script）、主題系統更完整。如果需要進階功能（例如 emoji picker、密碼管理器整合），Rofi 是更好的選擇。</p>
<h2 id="mako--dunst通知">Mako / Dunst：通知</h2>
<p>Mako 是 Wayland 原生的通知 daemon，負責<strong>顯示</strong>通知——它監聽 D-Bus 的 <code>org.freedesktop.Notifications</code> 介面、把收到的通知畫出來。產生通知的是應用程式，透過 <code>libnotify</code> 送上 D-Bus。所以一套能用的通知鏈需要兩半：daemon（顯示）和 <code>libnotify</code>（產生與遞送）。缺了 <code>libnotify</code>，連命令列自測用的 <code>notify-send</code> 都沒有。套件清單要同時列 <code>mako</code> 和 <code>libnotify</code>。</p>
<p>配置簡潔：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># ~/.config/mako/config</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="na">font</span><span class="o">=</span><span class="s">MesloLGS Nerd Font 11</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="na">background-color</span><span class="o">=</span><span class="s">#1e1e2e</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="na">text-color</span><span class="o">=</span><span class="s">#cdd6f4</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="na">border-color</span><span class="o">=</span><span class="s">#89b4fa</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="na">border-radius</span><span class="o">=</span><span class="s">8</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="na">border-size</span><span class="o">=</span><span class="s">2</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="na">padding</span><span class="o">=</span><span class="s">12</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="na">default-timeout</span><span class="o">=</span><span class="s">5000</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="na">max-visible</span><span class="o">=</span><span class="s">3</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">[urgency=critical]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="na">border-color</span><span class="o">=</span><span class="s">#f38ba8</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="na">default-timeout</span><span class="o">=</span><span class="s">0</span></span></span></code></pre></div><p>通知的視覺風格（圓角、配色、字型）要跟 waybar 和啟動器一致，這是整體 rice 不散的關鍵。</p>
<p>Nerd Font 的字符集只含 Latin、圖示與 Powerline 符號，不含中日韓。任何 CJK 文字（通知內文、視窗標題）若系統沒有 CJK 字型可 fallback 會變豆腐方塊。修法是安裝 CJK fallback（如 <code>noto-fonts-cjk</code>），fontconfig 會自動補字、不需改各工具的 config。另外，中途補裝字型後已在跑的 daemon 需重啟才看得到——<code>reload</code> 類指令只重讀設定檔、不重建記憶體中的字型快照（原理見 <a href="/blog/linux/dotfile/knowledge-cards/font-availability-at-startup/" data-link-title="字型的可用集合在 process 啟動時決定" data-link-desc="裝了字型但應用程式 / 狀態列 / 通知還是看不到、還是豆腐時回來讀">字型的可用集合在 process 啟動時決定</a>）。</p>
<h2 id="grim--slurp截圖">Grim + Slurp：截圖</h2>
<p>Grim 負責截圖、Slurp 負責框選區域，兩者搭配使用。截圖結果透過 <code>wl-copy</code> 送進剪貼簿時，需要明確指定 MIME 型別：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 全螢幕截圖到剪貼簿</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">grim - <span class="p">|</span> wl-copy --type image/png
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 框選區域截圖到剪貼簿</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">grim -g <span class="s2">&#34;</span><span class="k">$(</span>slurp<span class="k">)</span><span class="s2">&#34;</span> - <span class="p">|</span> wl-copy --type image/png
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 全螢幕截圖存檔</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">grim ~/screenshot.png</span></span></code></pre></div><p><code>wl-copy</code> 不帶 <code>--type</code> 時會嘗試透過 <code>xdg-utils</code>（<code>xdg-mime</code>）推斷 stdin 的型別。最小安裝環境沒有 <code>xdg-utils</code> 的情況下，PNG bytes 會被誤標成 <code>text/plain</code>，貼進影像應用程式就拿不到圖。明確帶 <code>--type image/png</code> 讓行為不依賴環境是否安裝了 <code>xdg-utils</code>。確認剪貼簿內容型別：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">wl-paste --list-types
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 應顯示 image/png</span></span></span></code></pre></div><p>Hyprland keybind 範例：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">bind</span> <span class="o">=</span> , Print, exec, grim - <span class="p">|</span> wl-copy --type image/png
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">bind</span> <span class="o">=</span> SHIFT, Print, exec, grim -g <span class="s2">&#34;</span><span class="k">$(</span>slurp<span class="k">)</span><span class="s2">&#34;</span> - <span class="p">|</span> wl-copy --type image/png</span></span></code></pre></div>]]></content:encoded></item><item><title>在 Hyprland 加圖形檔案管理員：依賴足跡與桌面環境耦合</title><link>https://tarrragon.github.io/blog/linux/tools/gui/gui-file-manager-dependencies/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/tools/gui/gui-file-manager-dependencies/</guid><description>&lt;p>在一個最小化的 Hyprland 環境加一個圖形應用，真正的成本不是那個 app 本身，是它拖進來的相依樹。Hyprland 這類 window manager 刻意不預設桌面環境，所以你手上是一台「只有合成器、沒有 GNOME/KDE/Cinnamon 那層服務」的機器。這時候裝一個看似單純的圖形檔案管理員，不同實作拖進來的東西可以差一個數量級——因為有些檔案管理員假設某個桌面環境的服務就在旁邊，有些則刻意做成桌面無關的獨立程式。&lt;/p>
&lt;p>這篇用「圖形檔案管理員」當具體案例，但判讀方式適用於任何你想加進最小環境的桌面 app：先看它的相依樹拖進什麼，再決定值不值得。&lt;/p>
&lt;h2 id="三種實作的實測相依">三種實作的實測相依&lt;/h2>
&lt;p>同樣是「有側欄、有選單、能瀏覽掛載裝置」的圖形檔案管理員，三個主流實作在一台已有 GTK3 的 Arch ARM 機器上，各自會&lt;strong>新裝&lt;/strong>的套件數量如下（&lt;code>pacman -S --needed --print&lt;/code> 實測）：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>檔案管理員&lt;/th>
 &lt;th>出身&lt;/th>
 &lt;th>新裝套件數&lt;/th>
 &lt;th>會拖進的關鍵相依&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Thunar&lt;/td>
 &lt;td>XFCE&lt;/td>
 &lt;td>8&lt;/td>
 &lt;td>xfce4 基礎庫（exo、libxfce4util/ui、xfconf、startup-notification、libgtop）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PCManFM-Qt&lt;/td>
 &lt;td>LXQt&lt;/td>
 &lt;td>7&lt;/td>
 &lt;td>libfm-qt、layer-shell-qt、menu-cache、lxqt-menu-data&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Nemo&lt;/td>
 &lt;td>Cinnamon&lt;/td>
 &lt;td>36&lt;/td>
 &lt;td>cinnamon-desktop、xapp、整套 gvfs + udisks2（libblockdev 主 + 8 子模組、mdadm、parted…）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>差異的來源不是「檔案管理員本身多大」，Thunar 跟 Nemo 的主程式都在 7–10 MB 量級。差的是後面那條相依鏈。&lt;/p>
&lt;h3 id="thunar-與-pcmanfm-qt桌面無關的獨立程式">Thunar 與 PCManFM-Qt：桌面無關的獨立程式&lt;/h3>
&lt;p>Thunar 與 PCManFM-Qt 都是刻意做成「不依賴完整桌面環境」的檔案管理員。Thunar 雖然出身 XFCE，但它拖進的 8 個套件是 XFCE 的&lt;strong>基礎函式庫&lt;/strong>（設定系統 xfconf、工具庫 libxfce4util、UI 庫 libxfce4ui），不是 XFCE 桌面本身——你不會因此裝到 XFCE 的面板、視窗管理器或 session。PCManFM-Qt 走 Qt 棧，帶的 &lt;code>layer-shell-qt&lt;/code> 反而是 Wayland 原生整合的加分項。這兩個裝下去，機器還是那台只有 Hyprland 的機器，只是多了一個能開的檔案管理員。&lt;/p>
&lt;h3 id="nemo為-cinnamon-而生假設-cinnamon-在旁邊">Nemo：為 Cinnamon 而生，假設 Cinnamon 在旁邊&lt;/h3>
&lt;p>Nemo 是 Cinnamon 桌面的檔案管理員，它的相依反映了這個出身：&lt;code>cinnamon-desktop&lt;/code> 提供背景與顯示設定的整合、&lt;code>xapp&lt;/code> 是 Cinnamon 系列跨桌面的整合層。即使你只想要「開一個視窗看檔案」，這些桌面元件也會一起裝上，因為 Nemo 在程式碼層面就假設它們存在。這不是 Nemo 寫得差，是它本來就不是設計給「裸 window manager」用的——它預期自己跑在 Cinnamon session 裡。&lt;/p>
&lt;p>Nemo 那 36 個套件裡，還有一大塊來自它把 &lt;code>gvfs&lt;/code> 列成硬相依（下一節說明 gvfs 是什麼），而 gvfs 又拖進整套磁碟管理棧（udisks2、libblockdev 的 8 個子模組、mdadm、parted、volume_key）。所以 Nemo 的相依樹是「Cinnamon 桌面元件」加「完整磁碟/檔案系統管理」兩層疊起來的結果。&lt;/p>
&lt;h2 id="gvfs側欄那些功能不是檔案管理員自己做的">gvfs：側欄那些功能不是檔案管理員自己做的&lt;/h2>
&lt;p>截圖裡檔案管理員側欄常見的「Devices（掛載裝置）」「Network（瀏覽網路芳鄰）」「Trash（垃圾桶）」，多半不是檔案管理員自己實作的，是 &lt;strong>gvfs（GNOME Virtual File System）&lt;/strong> 這個後端提供的。gvfs 用一層虛擬檔案系統把「掛載 USB 隨身碟」「連 SMB 網路分享」「把檔案丟垃圾桶」這些操作抽象成統一介面，讓檔案管理員不必自己處理每一種裝置與協定。&lt;/p>
&lt;p>這帶出一個重要判讀：&lt;strong>輕量不是免費的，當功能對等時，相依會靠攏。&lt;/strong> Thunar 與 PCManFM-Qt 把 gvfs 列成 optional dependency——不裝也能開檔案管理員，但側欄就沒有掛載、垃圾桶、網路那些功能。要讓輕量檔案管理員有截圖裡那種完整側欄，你得自己補 gvfs，而補上 gvfs 就會連帶拖進它的相依（udisks2、polkit、fuse3、libsecret 等）。Nemo 把 gvfs 設成硬相依，只是把這個選擇替你做了。&lt;/p>
&lt;p>所以公平的比較不是「Thunar 8 個 vs Nemo 36 個」，而是「Thunar + gvfs + 縮圖 vs Nemo」。補齊功能後，Thunar 這條路線仍然省下的是 Nemo 獨有的那層——&lt;code>cinnamon-desktop&lt;/code>、&lt;code>xapp&lt;/code>、&lt;code>xapp-symbolic-icons&lt;/code> 這些桌面環境耦合元件。那層，才是「為了一個檔案管理員裝半個 Cinnamon」真正可以省掉的部分。&lt;/p>
&lt;h2 id="tumbler縮圖也是一個額外套件">tumbler：縮圖也是一個額外套件&lt;/h2>
&lt;p>檔案管理員顯示圖片/影片縮圖，同樣不是內建的，靠的是縮圖服務。Thunar 家族用 &lt;code>tumbler&lt;/code>，影片縮圖再另外需要 &lt;code>ffmpegthumbnailer&lt;/code>。這是「一個功能對應一個額外套件」的又一個例子——最小環境裡，縮圖、掛載、網路瀏覽每一項都是你明確選擇要不要付相依成本的功能，而不是預設就有。&lt;/p>
&lt;h2 id="wayland--hyprland-下的注意事項">Wayland / Hyprland 下的注意事項&lt;/h2>
&lt;p>這些檔案管理員多數是 X11 時代的 GTK/Qt 程式，在 Wayland 下會透過 XWayland 或原生 Wayland 後端執行。PCManFM-Qt 帶的 &lt;code>layer-shell-qt&lt;/code> 是 Wayland 的 layer-shell 整合；GTK 的 Thunar/Nemo 在 Wayland 下一般走 GTK 自己的 Wayland 後端。開啟/儲存檔案對話框、拖放、縮圖預覽在裸 Hyprland（沒有完整 portal 服務）下的實際行為，取決於有沒有裝 &lt;code>xdg-desktop-portal&lt;/code> 與對應的後端。&lt;/p></description><content:encoded><![CDATA[<p>在一個最小化的 Hyprland 環境加一個圖形應用，真正的成本不是那個 app 本身，是它拖進來的相依樹。Hyprland 這類 window manager 刻意不預設桌面環境，所以你手上是一台「只有合成器、沒有 GNOME/KDE/Cinnamon 那層服務」的機器。這時候裝一個看似單純的圖形檔案管理員，不同實作拖進來的東西可以差一個數量級——因為有些檔案管理員假設某個桌面環境的服務就在旁邊，有些則刻意做成桌面無關的獨立程式。</p>
<p>這篇用「圖形檔案管理員」當具體案例，但判讀方式適用於任何你想加進最小環境的桌面 app：先看它的相依樹拖進什麼，再決定值不值得。</p>
<h2 id="三種實作的實測相依">三種實作的實測相依</h2>
<p>同樣是「有側欄、有選單、能瀏覽掛載裝置」的圖形檔案管理員，三個主流實作在一台已有 GTK3 的 Arch ARM 機器上，各自會<strong>新裝</strong>的套件數量如下（<code>pacman -S --needed --print</code> 實測）：</p>
<table>
  <thead>
      <tr>
          <th>檔案管理員</th>
          <th>出身</th>
          <th>新裝套件數</th>
          <th>會拖進的關鍵相依</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Thunar</td>
          <td>XFCE</td>
          <td>8</td>
          <td>xfce4 基礎庫（exo、libxfce4util/ui、xfconf、startup-notification、libgtop）</td>
      </tr>
      <tr>
          <td>PCManFM-Qt</td>
          <td>LXQt</td>
          <td>7</td>
          <td>libfm-qt、layer-shell-qt、menu-cache、lxqt-menu-data</td>
      </tr>
      <tr>
          <td>Nemo</td>
          <td>Cinnamon</td>
          <td>36</td>
          <td>cinnamon-desktop、xapp、整套 gvfs + udisks2（libblockdev 主 + 8 子模組、mdadm、parted…）</td>
      </tr>
  </tbody>
</table>
<p>差異的來源不是「檔案管理員本身多大」，Thunar 跟 Nemo 的主程式都在 7–10 MB 量級。差的是後面那條相依鏈。</p>
<h3 id="thunar-與-pcmanfm-qt桌面無關的獨立程式">Thunar 與 PCManFM-Qt：桌面無關的獨立程式</h3>
<p>Thunar 與 PCManFM-Qt 都是刻意做成「不依賴完整桌面環境」的檔案管理員。Thunar 雖然出身 XFCE，但它拖進的 8 個套件是 XFCE 的<strong>基礎函式庫</strong>（設定系統 xfconf、工具庫 libxfce4util、UI 庫 libxfce4ui），不是 XFCE 桌面本身——你不會因此裝到 XFCE 的面板、視窗管理器或 session。PCManFM-Qt 走 Qt 棧，帶的 <code>layer-shell-qt</code> 反而是 Wayland 原生整合的加分項。這兩個裝下去，機器還是那台只有 Hyprland 的機器，只是多了一個能開的檔案管理員。</p>
<h3 id="nemo為-cinnamon-而生假設-cinnamon-在旁邊">Nemo：為 Cinnamon 而生，假設 Cinnamon 在旁邊</h3>
<p>Nemo 是 Cinnamon 桌面的檔案管理員，它的相依反映了這個出身：<code>cinnamon-desktop</code> 提供背景與顯示設定的整合、<code>xapp</code> 是 Cinnamon 系列跨桌面的整合層。即使你只想要「開一個視窗看檔案」，這些桌面元件也會一起裝上，因為 Nemo 在程式碼層面就假設它們存在。這不是 Nemo 寫得差，是它本來就不是設計給「裸 window manager」用的——它預期自己跑在 Cinnamon session 裡。</p>
<p>Nemo 那 36 個套件裡，還有一大塊來自它把 <code>gvfs</code> 列成硬相依（下一節說明 gvfs 是什麼），而 gvfs 又拖進整套磁碟管理棧（udisks2、libblockdev 的 8 個子模組、mdadm、parted、volume_key）。所以 Nemo 的相依樹是「Cinnamon 桌面元件」加「完整磁碟/檔案系統管理」兩層疊起來的結果。</p>
<h2 id="gvfs側欄那些功能不是檔案管理員自己做的">gvfs：側欄那些功能不是檔案管理員自己做的</h2>
<p>截圖裡檔案管理員側欄常見的「Devices（掛載裝置）」「Network（瀏覽網路芳鄰）」「Trash（垃圾桶）」，多半不是檔案管理員自己實作的，是 <strong>gvfs（GNOME Virtual File System）</strong> 這個後端提供的。gvfs 用一層虛擬檔案系統把「掛載 USB 隨身碟」「連 SMB 網路分享」「把檔案丟垃圾桶」這些操作抽象成統一介面，讓檔案管理員不必自己處理每一種裝置與協定。</p>
<p>這帶出一個重要判讀：<strong>輕量不是免費的，當功能對等時，相依會靠攏。</strong> Thunar 與 PCManFM-Qt 把 gvfs 列成 optional dependency——不裝也能開檔案管理員，但側欄就沒有掛載、垃圾桶、網路那些功能。要讓輕量檔案管理員有截圖裡那種完整側欄，你得自己補 gvfs，而補上 gvfs 就會連帶拖進它的相依（udisks2、polkit、fuse3、libsecret 等）。Nemo 把 gvfs 設成硬相依，只是把這個選擇替你做了。</p>
<p>所以公平的比較不是「Thunar 8 個 vs Nemo 36 個」，而是「Thunar + gvfs + 縮圖 vs Nemo」。補齊功能後，Thunar 這條路線仍然省下的是 Nemo 獨有的那層——<code>cinnamon-desktop</code>、<code>xapp</code>、<code>xapp-symbolic-icons</code> 這些桌面環境耦合元件。那層，才是「為了一個檔案管理員裝半個 Cinnamon」真正可以省掉的部分。</p>
<h2 id="tumbler縮圖也是一個額外套件">tumbler：縮圖也是一個額外套件</h2>
<p>檔案管理員顯示圖片/影片縮圖，同樣不是內建的，靠的是縮圖服務。Thunar 家族用 <code>tumbler</code>，影片縮圖再另外需要 <code>ffmpegthumbnailer</code>。這是「一個功能對應一個額外套件」的又一個例子——最小環境裡，縮圖、掛載、網路瀏覽每一項都是你明確選擇要不要付相依成本的功能，而不是預設就有。</p>
<h2 id="wayland--hyprland-下的注意事項">Wayland / Hyprland 下的注意事項</h2>
<p>這些檔案管理員多數是 X11 時代的 GTK/Qt 程式，在 Wayland 下會透過 XWayland 或原生 Wayland 後端執行。PCManFM-Qt 帶的 <code>layer-shell-qt</code> 是 Wayland 的 layer-shell 整合；GTK 的 Thunar/Nemo 在 Wayland 下一般走 GTK 自己的 Wayland 後端。開啟/儲存檔案對話框、拖放、縮圖預覽在裸 Hyprland（沒有完整 portal 服務）下的實際行為，取決於有沒有裝 <code>xdg-desktop-portal</code> 與對應的後端。</p>
<blockquote>
<p><strong>[待實機驗證]</strong> 以下行為尚未在本系列的 Hyprland 實機環境確認，先標記待驗證：(1) Thunar 補上 gvfs 後，側欄的 Devices/Network/Trash 是否如預期出現並可用；(2) tumbler + ffmpegthumbnailer 的縮圖在 Wayland 下是否正常產生；(3) 三者在裸 Hyprland（無完整桌面 portal）下的檔案對話框與拖放行為；(4) Nemo 在沒有 Cinnamon session 的情況下，桌面圖示、設定整合等功能是否失效或報錯。這些是「相依裝了之後實際好不好用」的問題，相依數量本身（上表）已是實測確定值。</p></blockquote>
<h2 id="風險與注意事項">風險與注意事項</h2>
<p><strong>移除後的孤兒套件</strong>：裝了 Nemo 再反悔移除時，<code>cinnamon-desktop</code>、<code>xapp</code> 那一票被拖進來的相依會變成沒人依賴的孤兒（<code>pacman -Qtd</code> 可列出）。用 <code>pacman -Rns nemo</code> 移除時帶走遞迴相依，或定期清孤兒，否則那半個 Cinnamon 會留在系統裡。輕量檔案管理員因為拖進的東西少，這個問題也小。</p>
<p><strong>桌面環境服務未跑的副作用</strong>：把為某個桌面環境寫的 app 裝進裸 window manager，它預期的那些服務不在時，部分功能可能靜默失效或在啟動時報錯。這類問題不會在相依解析階段出現——套件裝得起來，是執行時才發現某個整合功能沒作用。（Nemo 在無 Cinnamon 下的具體表現，見上方待驗證項。）</p>
<p><strong>選型判準</strong>：最小化的 Hyprland 想要一個圖形檔案管理員，優先考慮桌面無關的 Thunar 或 PCManFM-Qt；需要截圖那種完整側欄功能時，明確補上 <code>gvfs</code>（掛載/垃圾桶/網路）與 <code>tumbler</code>（縮圖），把相依成本花在你真的要用的功能上。以 Thunar 為例，完整一套是 <code>pacman -S thunar gvfs tumbler ffmpegthumbnailer</code>（<code>gvfs</code> 給掛載/垃圾桶/網路、<code>tumbler</code> + <code>ffmpegthumbnailer</code> 給圖片與影片縮圖）。除非你本來就跑 Cinnamon，否則不建議為了單一檔案管理員把 Nemo 的整套桌面元件裝進來——那是付了桌面環境耦合的代價，卻沒用到那個桌面環境。</p>
<h2 id="待實機驗證清單">待實機驗證清單</h2>
<p>這篇的相依數量與相依樹是實測確定的；以下「裝了之後實際體驗」的部分待在 Hyprland 實機補驗證：</p>
<ul>
<li>Thunar + gvfs + tumbler + ffmpegthumbnailer 的完整側欄與縮圖行為</li>
<li>三種檔案管理員在裸 Hyprland（XWayland vs 原生 Wayland、portal 有無）下的差異</li>
<li>Nemo 脫離 Cinnamon session 的功能缺損範圍</li>
<li>加進 <code>packages-arch.txt</code> 後，bootstrap 一鍵安裝這條路線的實際落地結果</li>
</ul>
<h2 id="為什麼拿-nemo-當重的代表">為什麼拿 Nemo 當「重」的代表</h2>
<p>上表用 Nemo 當桌面環境耦合的代表，是因為它把耦合展示得最乾淨——<code>cinnamon-desktop</code> + <code>xapp</code> 那層桌面元件加上 gvfs 硬相依，剛好對照出 Thunar / PCManFM-Qt 省掉的是什麼。但它不是最重的：GNOME 的 Nautilus（<code>nautilus</code>）與 KDE 的 Dolphin（<code>dolphin</code>）是更 canonical 的「裝檔案管理員就拖進半個桌面」例子。Nautilus 深度綁 GNOME 的 GTK4 / libadwaita / tracker 索引棧，在非 GNOME 環境裝它會拉進一串 GNOME 平台庫；Dolphin 綁 KDE 的 Frameworks（KIO、Baloo 等），在非 KDE 環境同理。判讀方式跟 Nemo 一節相同：檔案管理員的相依樹反映它預期跑在哪個桌面裡。真要在裸 Hyprland 上驗，<code>pacman -S --needed --print nautilus</code> / <code>dolphin</code> 會列出各自那串平台庫——數量通常比 Nemo 更可觀（實測 Nautilus 72、Dolphin 91，約 Nemo 的 2 到 2.5 倍）。</p>
<h2 id="零相依對照純終端機檔案管理員">零相依對照：純終端機檔案管理員</h2>
<p>如果你在意的正是相依足跡，還有一條完全繞開圖形棧的路：終端機檔案管理員（<code>yazi</code> / <code>lf</code> / <code>nnn</code> / <code>ranger</code>）在終端機裡跑，不需要 GTK / Qt / gvfs / 桌面環境那一整套。<code>yazi</code>（Rust、內建預覽與非同步 I/O）與 <code>lf</code>、<code>nnn</code>（C、極小）在裸 Hyprland 或純 SSH 環境下都是近乎零額外相依的選擇——你已經有終端機，它們就能跑。代價是純鍵盤操作、沒有圖形檔案管理員那種拖放與縮圖牆。這是「圖形 vs 終端機」的取捨，不是同一條路上的輕重之分：要滑鼠拖放、縮圖預覽走圖形檔案管理員；要零相依、鍵盤流、SSH 下也能用走 TUI。TUI 檔案管理員的比較見 <a href="../../cli/file-manager-tuis/">CLI 環境工具的檔案管理器 TUI</a>。</p>
<h2 id="下一步">下一步</h2>
<ul>
<li>這篇談的是「加桌面 app 時怎麼判讀相依成本」，套件清單本身怎麼設計、怎麼被 bootstrap 一鍵安裝，見 <a href="/blog/linux/dotfile/08-sync-bootstrap/bootstrap-script-packages/" data-link-title="Bootstrap Script 與套件清單管理" data-link-desc="寫 dotfile 的 install script、或整理「這台機器裝了什麼」的套件清單時回來讀">模組八：Bootstrap script 與套件清單</a>。</li>
<li>Hyprland 本體與配套工具的安裝，見 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-installation/" data-link-title="Hyprland 安裝與環境建置" data-link-desc="要在 Arch Linux 上從零安裝 Hyprland 桌面環境時回來讀">模組五：安裝與環境建置</a>。</li>
<li>這台 Hyprland 是在 VM 上建起來測的，VM 能測什麼、什麼要留到實機，見 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/" data-link-title="Hyprland VM 環境設定與測試矩陣" data-link-desc="要在 VM 裡測試 Hyprland 配置、或判斷某個設定該在 VM 還是實機驗證時回來讀">VM 環境設定與測試矩陣</a>。</li>
</ul>
]]></content:encoded></item><item><title>常見故障場景與恢復操作</title><link>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/</guid><description>&lt;p>這篇按故障場景組織，每個場景列出症狀、原因、恢復步驟和預防措施。出問題時按症狀找到對應場景，照步驟操作。&lt;/p>
&lt;h2 id="場景一hyprland-compositor-crash">場景一：Hyprland compositor crash&lt;/h2>
&lt;p>&lt;strong>症狀&lt;/strong>：所有視窗同時消失，螢幕變黑或回到 TTY 登入畫面。滑鼠鍵盤有反應（可以切 TTY），但沒有桌面。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：Compositor process 遇到 fatal error 被 kernel 終止。常見觸發條件包括 plugin 相容性問題、特定 Wayland 協議操作觸發的 bug、GPU driver 回傳異常狀態。&lt;/p>
&lt;p>&lt;strong>恢復步驟&lt;/strong>：&lt;/p>
&lt;p>注意：以下步驟中 &lt;code>killall Hyprland&lt;/code> 或重啟 Hyprland 會終止所有由 compositor 管理的視窗，未存檔的工作會遺失。如果可能，先透過 TTY 或 SSH 嘗試存檔（如 &lt;code>kill -USR1 &amp;lt;pid&amp;gt;&lt;/code> 對支援的應用程式觸發存檔）。&lt;/p>
&lt;ol>
&lt;li>&lt;code>Ctrl+Alt+F2&lt;/code> 切到 TTY2&lt;/li>
&lt;li>用你的帳號登入&lt;/li>
&lt;li>檢查 Hyprland 的最後錯誤訊息：&lt;/li>
&lt;/ol>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 通用方式（不管 Hyprland 怎麼啟動都有效）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">journalctl -b &lt;span class="p">|&lt;/span> grep -i hypr &lt;span class="p">|&lt;/span> tail -30
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 如果 Hyprland 是 systemd user unit，可以更精準地查：&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">journalctl --user -u hyprland -n &lt;span class="m">50&lt;/span> --no-pager&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="4">
&lt;li>重新啟動 Hyprland：&lt;/li>
&lt;/ol>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Hyprland&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="5">
&lt;li>如果反覆 crash，檢查最近改過的 config：&lt;/li>
&lt;/ol>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ~/.config/hypr
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">git diff &lt;span class="c1"># 如果 dotfile 有版控&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>預防&lt;/strong>：config 改動後用 &lt;code>hyprctl reload&lt;/code> 測試，不要直接重啟。啟用 plugin 前確認版本跟 Hyprland 版本相容。&lt;/p>
&lt;h2 id="場景二單一桌面工具掛了">場景二：單一桌面工具掛了&lt;/h2>
&lt;p>&lt;strong>症狀&lt;/strong>：狀態列（waybar）消失、啟動器（wofi/rofi）叫不出來、通知（mako/dunst）不跳了。桌面其他功能正常，視窗可以操作。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：這些工具各自是獨立的 process。掛了只影響自己的功能，不影響 compositor 或其他工具。常見原因是 config 語法錯誤（改完 config 後觸發）、記憶體洩漏（長時間運作後）、或外部服務連線異常（如 waybar 的某個 module 連不到系統匯流排）。&lt;/p>
&lt;p>&lt;strong>恢復步驟&lt;/strong>：&lt;/p>
&lt;p>判斷啟動方式：如果工具是在 Hyprland config 裡用 &lt;code>exec-once&lt;/code>（Hyprland 的自動啟動指令，compositor 啟動時執行一次）啟動的，用 &lt;code>killall&lt;/code> + 手動重啟；如果是 systemd user unit，用 &lt;code>systemctl --user&lt;/code>。&lt;/p>
&lt;p>&lt;code>exec-once&lt;/code> 啟動方式（多數 Hyprland 安裝的預設做法）：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># waybar 掛了&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">killall waybar&lt;span class="p">;&lt;/span> waybar &lt;span class="p">&amp;amp;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># wofi 掛了&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">killall wofi
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1"># wofi 只在需要時啟動，不用常駐&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># mako 掛了&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">killall mako&lt;span class="p">;&lt;/span> mako &lt;span class="p">&amp;amp;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>systemd user unit 啟動方式：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">systemctl --user restart waybar
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">systemctl --user restart mako&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>確認工具是否在跑&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">pgrep waybar &lt;span class="c1"># 有輸出 = 在跑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">pgrep mako &lt;span class="c1"># 沒輸出 = 沒在跑&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>預防&lt;/strong>：改 config 後重啟對應的工具確認語法正確。Waybar 的 config 是 JSON 格式，語法錯誤會導致它無法啟動——改完後先用 &lt;code>waybar&lt;/code> 前台跑一次看有沒有錯誤訊息。&lt;/p>
&lt;h2 id="場景二點五鎖屏卡死hyprlock-異常結束">場景二點五：鎖屏卡死（hyprlock 異常結束）&lt;/h2>
&lt;p>&lt;strong>症狀&lt;/strong>：鎖屏畫面消失但桌面沒回來，螢幕顯示 Hyprland 的失效保護訊息（「it looks like you locked your screen but the lockscreen app died」），或畫面全黑但系統有回應（SSH 能連、TTY 可能切得到也可能切不到）。&lt;/p>
&lt;p>&lt;strong>原因&lt;/strong>：鎖屏工具（Hyprlock、Swaylock）透過 Wayland 的 ext-session-lock 協議向 compositor 請求鎖定。鎖定狀態由 compositor 持有，唯一正常解鎖動作是鎖屏 client 通過認證後呼叫 unlock_and_destroy。如果鎖屏 client 在持鎖狀態下被殺（&lt;code>pkill&lt;/code>、crash），compositor 沒收到認證信號，會維持鎖定並顯示失效保護畫面。這跟殺 waybar/mako 不同——那些是普通 process，殺了重啟就好；鎖屏 client 持有安全狀態，殺了反而卡住。&lt;/p></description><content:encoded><![CDATA[<p>這篇按故障場景組織，每個場景列出症狀、原因、恢復步驟和預防措施。出問題時按症狀找到對應場景，照步驟操作。</p>
<h2 id="場景一hyprland-compositor-crash">場景一：Hyprland compositor crash</h2>
<p><strong>症狀</strong>：所有視窗同時消失，螢幕變黑或回到 TTY 登入畫面。滑鼠鍵盤有反應（可以切 TTY），但沒有桌面。</p>
<p><strong>原因</strong>：Compositor process 遇到 fatal error 被 kernel 終止。常見觸發條件包括 plugin 相容性問題、特定 Wayland 協議操作觸發的 bug、GPU driver 回傳異常狀態。</p>
<p><strong>恢復步驟</strong>：</p>
<p>注意：以下步驟中 <code>killall Hyprland</code> 或重啟 Hyprland 會終止所有由 compositor 管理的視窗，未存檔的工作會遺失。如果可能，先透過 TTY 或 SSH 嘗試存檔（如 <code>kill -USR1 &lt;pid&gt;</code> 對支援的應用程式觸發存檔）。</p>
<ol>
<li><code>Ctrl+Alt+F2</code> 切到 TTY2</li>
<li>用你的帳號登入</li>
<li>檢查 Hyprland 的最後錯誤訊息：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 通用方式（不管 Hyprland 怎麼啟動都有效）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">journalctl -b <span class="p">|</span> grep -i hypr <span class="p">|</span> tail -30
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 如果 Hyprland 是 systemd user unit，可以更精準地查：</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">journalctl --user -u hyprland -n <span class="m">50</span> --no-pager</span></span></code></pre></div><ol start="4">
<li>重新啟動 Hyprland：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">Hyprland</span></span></code></pre></div><ol start="5">
<li>如果反覆 crash，檢查最近改過的 config：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">cd</span> ~/.config/hypr
</span></span><span class="line"><span class="ln">2</span><span class="cl">git diff  <span class="c1"># 如果 dotfile 有版控</span></span></span></code></pre></div><p><strong>預防</strong>：config 改動後用 <code>hyprctl reload</code> 測試，不要直接重啟。啟用 plugin 前確認版本跟 Hyprland 版本相容。</p>
<h2 id="場景二單一桌面工具掛了">場景二：單一桌面工具掛了</h2>
<p><strong>症狀</strong>：狀態列（waybar）消失、啟動器（wofi/rofi）叫不出來、通知（mako/dunst）不跳了。桌面其他功能正常，視窗可以操作。</p>
<p><strong>原因</strong>：這些工具各自是獨立的 process。掛了只影響自己的功能，不影響 compositor 或其他工具。常見原因是 config 語法錯誤（改完 config 後觸發）、記憶體洩漏（長時間運作後）、或外部服務連線異常（如 waybar 的某個 module 連不到系統匯流排）。</p>
<p><strong>恢復步驟</strong>：</p>
<p>判斷啟動方式：如果工具是在 Hyprland config 裡用 <code>exec-once</code>（Hyprland 的自動啟動指令，compositor 啟動時執行一次）啟動的，用 <code>killall</code> + 手動重啟；如果是 systemd user unit，用 <code>systemctl --user</code>。</p>
<p><code>exec-once</code> 啟動方式（多數 Hyprland 安裝的預設做法）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># waybar 掛了</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">killall waybar<span class="p">;</span> waybar <span class="p">&amp;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># wofi 掛了</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">killall wofi
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># wofi 只在需要時啟動，不用常駐</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># mako 掛了</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl">killall mako<span class="p">;</span> mako <span class="p">&amp;</span></span></span></code></pre></div><p>systemd user unit 啟動方式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">systemctl --user restart waybar
</span></span><span class="line"><span class="ln">2</span><span class="cl">systemctl --user restart mako</span></span></code></pre></div><p><strong>確認工具是否在跑</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pgrep waybar  <span class="c1"># 有輸出 = 在跑</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">pgrep mako    <span class="c1"># 沒輸出 = 沒在跑</span></span></span></code></pre></div><p><strong>預防</strong>：改 config 後重啟對應的工具確認語法正確。Waybar 的 config 是 JSON 格式，語法錯誤會導致它無法啟動——改完後先用 <code>waybar</code> 前台跑一次看有沒有錯誤訊息。</p>
<h2 id="場景二點五鎖屏卡死hyprlock-異常結束">場景二點五：鎖屏卡死（hyprlock 異常結束）</h2>
<p><strong>症狀</strong>：鎖屏畫面消失但桌面沒回來，螢幕顯示 Hyprland 的失效保護訊息（「it looks like you locked your screen but the lockscreen app died」），或畫面全黑但系統有回應（SSH 能連、TTY 可能切得到也可能切不到）。</p>
<p><strong>原因</strong>：鎖屏工具（Hyprlock、Swaylock）透過 Wayland 的 ext-session-lock 協議向 compositor 請求鎖定。鎖定狀態由 compositor 持有，唯一正常解鎖動作是鎖屏 client 通過認證後呼叫 unlock_and_destroy。如果鎖屏 client 在持鎖狀態下被殺（<code>pkill</code>、crash），compositor 沒收到認證信號，會維持鎖定並顯示失效保護畫面。這跟殺 waybar/mako 不同——那些是普通 process，殺了重啟就好；鎖屏 client 持有安全狀態，殺了反而卡住。</p>
<p><strong>恢復步驟</strong>：</p>
<ol>
<li>嘗試切到另一個 TTY（<code>Ctrl+Alt+F2</code>）。注意：ext-session-lock 的安全語意允許 compositor 攔截 VT 切換快捷鍵，此時 TTY 切不過去，改用 SSH 從另一台機器連入</li>
<li>允許新的鎖屏 client 接管既有的鎖：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">hyprctl --instance <span class="m">0</span> <span class="s1">&#39;keyword misc:allow_session_lock_restore 1&#39;</span></span></span></code></pre></div><ol start="3">
<li>重新拉一個鎖屏 client：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">hyprctl --instance <span class="m">0</span> <span class="s1">&#39;dispatch exec hyprlock&#39;</span></span></span></code></pre></div><ol start="4">
<li>回到鎖屏畫面，用密碼正常解鎖</li>
</ol>
<p><strong>判讀</strong>：<code>loginctl show-session &lt;id&gt; -p LockedHint</code> 可能顯示 <code>LockedHint=no</code>（logind 層認為沒鎖），但畫面仍進不去——因為擋住畫面的是 compositor 的 ext-session-lock，跟 logind 的提示是獨立的兩層。判斷畫面鎖定狀態看 compositor 層，不看 logind。</p>
<p><strong>預防</strong>：測試鎖屏時備好恢復路徑（知道密碼、或預先開 SSH）。不要用殺 process 的方式結束鎖屏——要結束就走認證解鎖。自動化流程若會啟動鎖屏，把「需要人工解鎖」算進代價。鎖屏安全模型的完整說明見 <a href="/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock</a>。</p>
<h2 id="場景二點六桌面-shell-畫得出來但互動死掉進程活著卻-wedged">場景二點六：桌面 shell 畫得出來但互動死掉（進程活著卻 wedged）</h2>
<p><strong>症狀</strong>：bar / 狀態列還在螢幕上、看起來一切正常，但點它的按鈕（工作區切換、系統匣圖示）沒反應，keybind 叫不出啟動器（wofi / 內建 launcher）。同時焦點視窗（例如終端機）打字完全正常——鍵盤到得了應用程式，只是桌面 shell 的互動死了。</p>
<p><strong>原因</strong>：這是跟場景二（工具掛了）不同的一類故障，關鍵差別在<strong>進程還活著</strong>。場景二是 process 崩潰退出（<code>pgrep</code> 沒輸出），殺了重啟就好；這裡的桌面 shell（如 caelestia / Quickshell）進程還在跑（<code>pgrep</code> 找得到、STAT 是正常的 <code>S</code>、在 <code>poll</code> 等事件、CPU 不高），但它內部的某個子系統初始化失敗了——常見是 QML scene 的某個物件因為上游錯誤沒建起來、變成 null，於是負責「keybind → 開抽屜」「bar 按鈕互動」的模組對 null 讀屬性、整條互動接線死掉。bar 之所以還畫得出來，是它還停在初始化失敗前那一幀的畫面：<strong>畫得出來不等於還活著</strong>，跟鎖屏那課（畫面有密碼框不等於真的在鎖）是同一個陷阱。</p>
<p>上游觸發常是渲染層。實測案例：VM 的 GPU 只提供到 GLSL 1.20，而 shell 的 shader 需要 GLES 100/300/330，pipeline 建不起來（log 狂噴 <code>Failed to build graphics pipeline state</code>），這次渲染失敗把 scene 初始化打斷，drawers 狀態物件變 null。</p>
<p><strong>診斷（別看 pgrep，讀 shell 自己的 log）</strong>：</p>
<p><code>pgrep</code> 在這裡會騙你——它回報「在跑」，但那不等於「在運作」。權威來源是 shell 自己的 log，而且這種 log 常常<strong>不在 journalctl、也不在你猜的路徑</strong>，要用該 shell 專屬的 log 指令：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># caelestia 的例子：用它自己的 CLI 印 shell log</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">caelestia shell -l 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> tail -40
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 看的是 QML 的 TypeError：對 null 讀屬性 = 那個子系統死了</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">#   scene: @modules/Shortcuts.qml: TypeError: Cannot read property &#39;launcher&#39; of null</span></span></span></code></pre></div><p>另一個活性探針是 shell 的 <strong>IPC 回不回真實狀態</strong>：正常時查抽屜列表會回傳名字，子系統死掉時回空——這比「進程在不在」精準得多：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 子系統活著 → 列出 bar/launcher/session…；死掉 → 回空</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">caelestia shell ipc call drawers list</span></span></code></pre></div><p><strong>恢復步驟</strong>：重啟 shell 讓 scene 重建。以 caelestia 為例：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">caelestia shell -k     <span class="c1"># 殺掉卡住的 shell</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">caelestia shell -d     <span class="c1"># 重新啟動（detached）</span></span></span></code></pre></div><p><strong>驗證修好了，看子系統回來、不是看 pgrep</strong>：重啟後 process 一定在（<code>pgrep</code> 本來就會有），要確認的是接線恢復——<code>caelestia shell ipc call drawers list</code> 從「回空」變成列出真實抽屜名、log 不再噴 null 的 TypeError。這對應「重啟成功要驗子系統狀態、不是驗 process 存在」的通用紀律。</p>
<p><strong>判讀與其他場景的界線</strong>：<code>pgrep</code> 有輸出 + bar 畫得出來 → 別急著判「正常」；點不動 / keybind 死掉就是 wedged 的訊號，往 shell 自己的 log 查。這跟場景二（process 真的沒了、<code>pgrep</code> 空）、場景三（compositor 整個凍結、連終端機打字都不行）都不同——這裡 compositor 正常、焦點視窗鍵盤正常，只有 shell 的互動接線死。判「進程活著到底有沒有在運作」的通用招式，見 <a href="/blog/linux/debug/process-service-state-diagnosis/" data-link-title="程序、服務與狀態怎麼判" data-link-desc="要判斷一個程式活著沒、某個系統服務現在由誰提供、桌面 session 有沒有被鎖、或終端機多工器的 session 還在不在時，用對的權威來源而不是靠畫面或猜的名字">程序、服務與狀態怎麼判</a>。</p>
<p><strong>預防</strong>：留意 shell log 裡持續出現的 shader / 渲染 pipeline 錯誤——在 VM 或 GL 支援不足的環境，這類錯誤可能非致命地存在（shell 大致能用），但一次渲染失敗就可能打斷 scene 初始化、把互動接線弄死。VM 環境要確認 GPU 提供的 GL / GLSL 版本足夠（virtio-gpu 走 mesa/zink 提供 GL 3.3+），或在 shell 設定關掉需要高階 shader 的效果。</p>
<h2 id="場景三gpu-driver-hang畫面凍結">場景三：GPU driver hang（畫面凍結）</h2>
<p><strong>症狀</strong>：桌面畫面完全凍結——滑鼠不動、鍵盤不回應、<code>Ctrl+Alt+F2</code> 切 TTY 也沒反應或延遲很久才回應。但如果從另一台機器 SSH 進來，系統是活的，process 都在跑。</p>
<p><strong>原因</strong>：GPU driver 進入異常狀態。NVIDIA 閉源驅動在 Linux 上的穩定性不如 AMD 開源驅動（amdgpu），特別是在 Wayland 環境下。常見觸發條件包括 suspend/resume 之後 GPU 沒正確恢復、某些 OpenGL/Vulkan 操作觸發 driver bug、顯示輸出切換（接上或拔掉外接螢幕）。</p>
<p><strong>恢復步驟</strong>：</p>
<p>方法 A — 如果 TTY 能切過去：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 切到 TTY2</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">Ctrl+Alt+F2
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 殺掉 Hyprland（它會帶走所有視窗）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">killall Hyprland
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 重新啟動</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">Hyprland</span></span></code></pre></div><p>方法 B — 如果 TTY 也凍結、但 SSH 能連：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 從另一台機器 SSH 進來（需事先知道 IP，見下方預防段）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">ssh user@machine-ip
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 殺掉 compositor</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">killall Hyprland
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 如果需要 reset GPU（NVIDIA，且 driver 仍回應）</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"># 前提：所有使用 GPU 的 process 已停止（compositor 已 kill）</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">sudo nvidia-smi --gpu-reset
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 切回機器前面重啟 Hyprland</span></span></span></code></pre></div><p>方法 C — 如果完全無回應，先嘗試 Magic SysRq：</p>
<p>Magic SysRq 是 kernel 層級的緊急操作介面，即使 userspace 完全卡死也能回應。按住 <code>Alt+SysRq</code>（筆電通常是 <code>Alt+Fn+SysRq</code>），然後依序按 <code>R E I S U B</code>（每個鍵間隔幾秒）：</p>
<ul>
<li><code>R</code> — 把鍵盤從 raw mode 搶回來</li>
<li><code>E</code> — 對所有 process 送 SIGTERM</li>
<li><code>I</code> — 對所有 process 送 SIGKILL</li>
<li><code>S</code> — sync 所有檔案系統</li>
<li><code>U</code> — remount 所有檔案系統為 read-only</li>
<li><code>B</code> — 立即 reboot</li>
</ul>
<p>這比直接斷電安全——sync + unmount 步驟會盡量保護磁碟上的資料。Arch Linux 預設可能停用 SysRq，需在 <code>/etc/sysctl.d/</code> 設定 <code>kernel.sysrq=1</code> 啟用。</p>
<p>方法 D — 如果 SysRq 也無效，按住電源鍵強制關機：</p>
<p>這是最後手段。Linux 的 ext4/btrfs 檔案系統有 journal 保護，強制關機通常不會損壞<strong>檔案系統結構</strong>。但 journal 保護的是 metadata 一致性，正在寫入的使用者資料（未存檔的文件、正在下載的檔案）仍然可能遺失或損壞。重開機後正常登入 TTY、啟動 Hyprland 即可。如果開機過程有異常，用 <code>journalctl -b -1 -p err</code> 查看上次開機的錯誤訊息，確認是否有檔案系統修復紀錄。</p>
<p><strong>預防</strong>：</p>
<ul>
<li>NVIDIA 用戶：關注 driver 版本的 release notes，已知有 Wayland 問題的版本避開</li>
<li>配置 suspend 後的 GPU 恢復：在 Hyprland config 或 systemd sleep hook 裡加入 GPU reset 操作</li>
<li>事先記錄機器的 IP 位址（<code>ip addr show</code>）或設定固定 hostname（如 mDNS 的 <code>machine.local</code>），桌面凍結時才有辦法從另一台機器 SSH 進來</li>
<li>考慮開啟 SSH server，出問題時可以遠端救援。開啟後應配置 key-based authentication 並停用密碼登入（<code>PasswordAuthentication no</code>），避免在網路上暴露密碼登入通道：</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl <span class="nb">enable</span> sshd
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo systemctl start sshd
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 安全配置：停用密碼登入（確保已設好 SSH key 再改）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 編輯 /etc/ssh/sshd_config，設定 PasswordAuthentication no</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># 然後 sudo systemctl restart sshd</span></span></span></code></pre></div><h2 id="場景四記憶體耗盡oom">場景四：記憶體耗盡（OOM）</h2>
<p><strong>症狀</strong>：系統變得極慢，操作有明顯延遲（幾秒到幾十秒）。隨後可能某些 process 突然被殺掉——瀏覽器分頁消失、IDE 視窗關閉，嚴重時 Hyprland 本身被 OOM Killer 終止導致桌面消失。</p>
<p><strong>原因</strong>：實體記憶體和 swap 都用完了。常見觸發者是瀏覽器（Chrome/Firefox 的分頁越開越多）、IDE（大型專案的 language server）、Docker container、或應用程式的記憶體洩漏。</p>
<p><strong>恢復步驟</strong>：</p>
<p>如果還能操作：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 找出誰在吃記憶體</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">top -o %MEM
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 或用 htop/btop 的互動介面</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 殺掉佔最多記憶體的 process</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nb">kill</span> &lt;PID&gt;</span></span></code></pre></div><p>如果桌面已經被殺、在 TTY 裡：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 看 OOM Killer 殺了誰</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">journalctl -b <span class="p">|</span> grep -i <span class="s2">&#34;out of memory&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">journalctl -b <span class="p">|</span> grep -i <span class="s2">&#34;oom&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 清理完後重啟桌面</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">Hyprland</span></span></code></pre></div><p><strong>預防</strong>：</p>
<p>設定 swap（即使 RAM 夠大，swap 提供 OOM 前的緩衝時間讓你有機會手動清理 process）。RAM 16GB 以上的機器，2-4GB swap 作緩衝通常足夠：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 查看是否有 swap</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">swapon --show
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 如果沒有，建立一個 4GB 的 swap file（ext4 檔案系統）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">sudo fallocate -l 4G /swapfile
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">sudo chmod <span class="m">600</span> /swapfile
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">sudo mkswap /swapfile
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">sudo swapon /swapfile
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 永久生效：加入 /etc/fstab</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;/swapfile none swap defaults 0 0&#39;</span> <span class="p">|</span> sudo tee -a /etc/fstab</span></span></code></pre></div><p>Btrfs 檔案系統不支援 <code>fallocate</code> 建立 swap file。Btrfs 用戶需改用 <code>btrfs filesystem mkswapfile</code> 或建立專屬的 swap subvolume，具體做法參考 Arch Wiki 的 Btrfs swap 段落。</p>
<p>啟用 systemd-oomd（比 kernel OOM Killer 更早介入、更可控）。systemd-oomd 在 cgroup 的記憶體壓力達到閾值時就開始清理，預設配置對多數桌面場景足夠。進階調整可透過 <code>/etc/systemd/oomd.conf</code> 設定：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl <span class="nb">enable</span> systemd-oomd
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo systemctl start systemd-oomd</span></span></code></pre></div><h2 id="場景五config-寫錯導致-hyprland-啟動失敗">場景五：Config 寫錯導致 Hyprland 啟動失敗</h2>
<p><strong>症狀</strong>：從 display manager（圖形登入畫面，如 SDDM、GDM）登入後立刻黑屏又回到登入畫面，或直接回到 TTY。如果從 TTY 手動執行 <code>Hyprland</code>，看到錯誤訊息後立即退出。</p>
<p><strong>原因</strong>：Hyprland config 有語法錯誤或引用了不存在的資源。常見錯誤包括 <code>source</code> 指定的檔案不存在、keybind 語法寫錯、monitor 設定格式錯誤。</p>
<p><strong>恢復步驟</strong>：</p>
<ol>
<li>切到 TTY（<code>Ctrl+Alt+F2</code>）</li>
<li>登入後直接跑 Hyprland 看錯誤訊息：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 看 Hyprland 的啟動錯誤（也可用 journalctl -b | grep -i hypr）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">Hyprland
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># Hyprland 如果因 config 錯誤無法啟動，會直接印出錯誤訊息後退出</span></span></span></code></pre></div><ol start="3">
<li>根據錯誤訊息修改 config：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># Hyprland 的主 config</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">vim ~/.config/hypr/hyprland.conf
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 如果用了 source 拆分，錯誤訊息會指出是哪個檔案</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">vim ~/.config/hypr/keybinds.conf</span></span></code></pre></div><ol start="4">
<li>修完後重新啟動：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">Hyprland</span></span></code></pre></div><p><strong>常見 config 錯誤</strong>：</p>
<p><code>source</code> 路徑錯誤——檔案不存在或路徑拼錯：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 確認 source 指定的檔案都存在</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">grep <span class="s2">&#34;^source&#34;</span> ~/.config/hypr/hyprland.conf
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 逐一檢查每個路徑</span></span></span></code></pre></div><p>Monitor 設定錯誤——指定了不存在的螢幕名稱：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 查看系統實際的螢幕名稱</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 在能進桌面時記下來，或用 wlr-randr</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">wlr-randr</span></span></code></pre></div><p>Keybind 語法錯誤——dispatcher 名稱拼錯或參數格式不對。Hyprland 的 keybind 格式是 <code>bind = MOD, key, dispatcher, params</code>，少一個欄位或 dispatcher 拼錯就會報錯。</p>
<p><strong>預防</strong>：config 改動時用 <code>hyprctl reload</code> 即時測試，不要改完 config 就直接重啟 Hyprland。如果 dotfile 用 Git 管理，改壞了可以 <code>git checkout</code> 回退。</p>
<h2 id="場景六suspendresume-後桌面異常">場景六：Suspend/resume 後桌面異常</h2>
<p><strong>症狀</strong>：筆電蓋上或手動 suspend 後喚醒，出現以下任一情況——螢幕黑屏但系統有反應（鍵盤背光亮）、解析度跑掉、多螢幕配置丟失（所有視窗擠到一個螢幕）、compositor 直接 crash 回到 TTY。</p>
<p><strong>原因</strong>：GPU driver 在 suspend/resume 過程中需要保存和恢復 GPU 狀態。NVIDIA 閉源驅動在 Wayland 上的 suspend/resume 支援不如 AMD 開源驅動穩定，特別是多螢幕配置和高刷新率模式下容易出問題。</p>
<p><strong>恢復步驟</strong>：</p>
<p>如果螢幕黑屏但系統有反應：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 切到 TTY</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">Ctrl+Alt+F2
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 檢查 Hyprland 是否還在跑</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">pgrep Hyprland
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 如果在跑但沒畫面，kill 再重啟</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">killall Hyprland
</span></span><span class="line"><span class="ln">9</span><span class="cl">Hyprland</span></span></code></pre></div><p>如果解析度或螢幕配置跑掉：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 在 Hyprland 內重新套用 monitor 設定</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hyprctl reload</span></span></code></pre></div><p>如果 compositor 已經 crash：按場景一的步驟從 TTY 重啟。</p>
<p><strong>預防</strong>：</p>
<ul>
<li>NVIDIA 用戶：在 <code>/etc/modprobe.d/nvidia.conf</code> 啟用 preserve video memory allocations：</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># /etc/modprobe.d/nvidia.conf</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">options nvidia <span class="nv">NVreg_PreserveVideoMemoryAllocations</span><span class="o">=</span><span class="m">1</span></span></span></code></pre></div><p>同時啟用 NVIDIA 的 suspend/resume systemd service：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl <span class="nb">enable</span> nvidia-suspend
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo systemctl <span class="nb">enable</span> nvidia-resume
</span></span><span class="line"><span class="ln">3</span><span class="cl">sudo systemctl <span class="nb">enable</span> nvidia-hibernate</span></span></code></pre></div><ul>
<li>AMD 用戶：amdgpu driver 的 suspend/resume 通常開箱即用，遇到問題先更新 kernel（<code>pacman -Syu linux</code>）。</li>
</ul>
]]></content:encoded></item><item><title>Hyprland 核心配置</title><link>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/</guid><description>&lt;p>Hyprland v0.55+ 使用 Lua 作為配置語言。Lua 語法基礎見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/lua-scripting-language/" data-link-title="Lua 腳本語言" data-link-desc="在 Hyprland 或 Neovim 配置檔遇到 Lua 語法看不懂時回來讀 — 配置檔需要的最小 Lua 知識">Lua 腳本語言&lt;/a>。&lt;/p>
&lt;h2 id="配置檔位置與格式">配置檔位置與格式&lt;/h2>
&lt;p>Hyprland v0.55 起，配置格式從 hyprlang（&lt;code>.conf&lt;/code>）遷移到 &lt;strong>Lua&lt;/strong>（&lt;code>.lua&lt;/code>）。主配置檔是 &lt;code>~/.config/hypr/hyprland.lua&lt;/code>。如果 &lt;code>.lua&lt;/code> 不存在但 &lt;code>.conf&lt;/code> 存在，Hyprland 會回退讀取舊格式，但 hyprlang 已標記為棄用，後續版本會移除支援。&lt;/p>
&lt;p>修改後即時生效（不需要重新登入或重啟）。第一次啟動時如果沒有配置檔，Hyprland 會自動產生範例配置。即使配置有語法錯誤，緊急快捷鍵（SUPER+Q 開 terminal、SUPER+M 離開）仍然可用。&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>[VM 可測試]&lt;/strong> 配置檔格式和載入行為在 VM 裡跟實機完全相同。&lt;/p>&lt;/blockquote>
&lt;h3 id="模組化拆分">模組化拆分&lt;/h3>
&lt;p>用 Lua 原生的 &lt;code>require()&lt;/code> 拆分配置：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">-- ~/.config/hypr/hyprland.lua — 主入口&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;monitors&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">-- 載入 monitors.lua&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;keybinds&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">-- 載入 keybinds.lua&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;rules&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">-- 載入 rules.lua&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;autostart&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">-- 載入 autostart.lua&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;appearance&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">-- 載入 appearance.lua&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="n">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;env&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c1">-- 載入 env.lua&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>require(&amp;quot;monitors&amp;quot;)&lt;/code> 會從同目錄載入 &lt;code>monitors.lua&lt;/code>。拆分的理由是職責分離——改 keybind 不用在千行配置裡找位置，每個子檔案可以獨立 diff 和 rollback。&lt;/p>
&lt;h3 id="從舊格式遷移">從舊格式遷移&lt;/h3>
&lt;p>Hyprland 官方提供遷移工具：&lt;code>hyprlang2lua&lt;/code>（Go，也有瀏覽器版）和 &lt;code>hypr-migrate&lt;/code>。如果你是從零開始，直接用 Lua 格式。&lt;/p>
&lt;h2 id="monitor-設定">Monitor 設定&lt;/h2>
&lt;p>Monitor 設定是硬體相關的核心配置，每台機器都不同。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">-- ~/.config/hypr/monitors.lua&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="n">hl.config&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="n">monitor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 語法：&amp;#34;name, resolution@refreshrate, position, scale&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;DP-1, 2560x1440@144, 0x0, 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;HDMI-A-1, 1920x1080@60, 2560x0, 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 筆電內建螢幕&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;eDP-1, preferred, auto, 1.5&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 預設規則（未明確列出的螢幕）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;, preferred, auto, 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 鏡像模式（DP-2 鏡像 DP-1 的畫面）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- &amp;#34;DP-2, preferred, auto, 1, mirror, DP-1&amp;#34;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="p">})&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>position&lt;/code> 決定多螢幕的空間排列——&lt;code>0x0&lt;/code> 是左上角原點，&lt;code>2560x0&lt;/code> 表示在第一個螢幕的右邊。&lt;code>scale&lt;/code> 處理 HiDPI 顯示（1.5 表示 150% 縮放）。&lt;/p>
&lt;p>查詢可用的 monitor 名稱：&lt;code>hyprctl monitors&lt;/code>&lt;/p>
&lt;p>其他選項：&lt;/p>
&lt;ul>
&lt;li>&lt;code>transform, 1&lt;/code> — 旋轉（0=正常、1=90 度、2=180 度、3=270 度）&lt;/li>
&lt;li>&lt;code>vrr, 1&lt;/code> — 可變更新率（1=開啟、2=僅全螢幕）&lt;/li>
&lt;li>&lt;code>bitdepth, 10&lt;/code> — 10-bit 色彩&lt;/li>
&lt;/ul>
&lt;h3 id="筆電合蓋行為">筆電合蓋行為&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">-- 合蓋時關閉內建螢幕、開蓋時恢復&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">-- bindl = 即使輸入鎖定也能觸發（合蓋時需要）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="n">hl.bindl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;switch:on:Lid Switch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;exec&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;hyprctl keyword monitor &amp;#39;eDP-1, disable&amp;#39;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="n">hl.bindl&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;switch:off:Lid Switch&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;exec&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;hyprctl keyword monitor &amp;#39;eDP-1, preferred, auto, 1&amp;#39;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>[需實機測試]&lt;/strong> Monitor 位置排列、HiDPI 縮放、VRR、旋轉、多螢幕熱插拔、合蓋行為。VM 通常只有一個虛擬螢幕。&lt;/p></description><content:encoded><![CDATA[<p>Hyprland v0.55+ 使用 Lua 作為配置語言。Lua 語法基礎見 <a href="/blog/linux/dotfile/knowledge-cards/lua-scripting-language/" data-link-title="Lua 腳本語言" data-link-desc="在 Hyprland 或 Neovim 配置檔遇到 Lua 語法看不懂時回來讀 — 配置檔需要的最小 Lua 知識">Lua 腳本語言</a>。</p>
<h2 id="配置檔位置與格式">配置檔位置與格式</h2>
<p>Hyprland v0.55 起，配置格式從 hyprlang（<code>.conf</code>）遷移到 <strong>Lua</strong>（<code>.lua</code>）。主配置檔是 <code>~/.config/hypr/hyprland.lua</code>。如果 <code>.lua</code> 不存在但 <code>.conf</code> 存在，Hyprland 會回退讀取舊格式，但 hyprlang 已標記為棄用，後續版本會移除支援。</p>
<p>修改後即時生效（不需要重新登入或重啟）。第一次啟動時如果沒有配置檔，Hyprland 會自動產生範例配置。即使配置有語法錯誤，緊急快捷鍵（SUPER+Q 開 terminal、SUPER+M 離開）仍然可用。</p>
<blockquote>
<p><strong>[VM 可測試]</strong> 配置檔格式和載入行為在 VM 裡跟實機完全相同。</p></blockquote>
<h3 id="模組化拆分">模組化拆分</h3>
<p>用 Lua 原生的 <code>require()</code> 拆分配置：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">-- ~/.config/hypr/hyprland.lua — 主入口</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;monitors&#34;</span><span class="p">)</span>      <span class="c1">-- 載入 monitors.lua</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;keybinds&#34;</span><span class="p">)</span>      <span class="c1">-- 載入 keybinds.lua</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;rules&#34;</span><span class="p">)</span>         <span class="c1">-- 載入 rules.lua</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;autostart&#34;</span><span class="p">)</span>     <span class="c1">-- 載入 autostart.lua</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;appearance&#34;</span><span class="p">)</span>    <span class="c1">-- 載入 appearance.lua</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="n">require</span><span class="p">(</span><span class="s2">&#34;env&#34;</span><span class="p">)</span>           <span class="c1">-- 載入 env.lua</span></span></span></code></pre></div><p><code>require(&quot;monitors&quot;)</code> 會從同目錄載入 <code>monitors.lua</code>。拆分的理由是職責分離——改 keybind 不用在千行配置裡找位置，每個子檔案可以獨立 diff 和 rollback。</p>
<h3 id="從舊格式遷移">從舊格式遷移</h3>
<p>Hyprland 官方提供遷移工具：<code>hyprlang2lua</code>（Go，也有瀏覽器版）和 <code>hypr-migrate</code>。如果你是從零開始，直接用 Lua 格式。</p>
<h2 id="monitor-設定">Monitor 設定</h2>
<p>Monitor 設定是硬體相關的核心配置，每台機器都不同。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- ~/.config/hypr/monitors.lua</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">monitor</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="c1">-- 語法：&#34;name, resolution@refreshrate, position, scale&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="s2">&#34;DP-1, 2560x1440@144, 0x0, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="s2">&#34;HDMI-A-1, 1920x1080@60, 2560x0, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="c1">-- 筆電內建螢幕</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="s2">&#34;eDP-1, preferred, auto, 1.5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="c1">-- 預設規則（未明確列出的螢幕）</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="s2">&#34;, preferred, auto, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="c1">-- 鏡像模式（DP-2 鏡像 DP-1 的畫面）</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="c1">-- &#34;DP-2, preferred, auto, 1, mirror, DP-1&#34;,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p><code>position</code> 決定多螢幕的空間排列——<code>0x0</code> 是左上角原點，<code>2560x0</code> 表示在第一個螢幕的右邊。<code>scale</code> 處理 HiDPI 顯示（1.5 表示 150% 縮放）。</p>
<p>查詢可用的 monitor 名稱：<code>hyprctl monitors</code></p>
<p>其他選項：</p>
<ul>
<li><code>transform, 1</code> — 旋轉（0=正常、1=90 度、2=180 度、3=270 度）</li>
<li><code>vrr, 1</code> — 可變更新率（1=開啟、2=僅全螢幕）</li>
<li><code>bitdepth, 10</code> — 10-bit 色彩</li>
</ul>
<h3 id="筆電合蓋行為">筆電合蓋行為</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">-- 合蓋時關閉內建螢幕、開蓋時恢復</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">-- bindl = 即使輸入鎖定也能觸發（合蓋時需要）</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;switch:on:Lid Switch&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;hyprctl keyword monitor &#39;eDP-1, disable&#39;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;switch:off:Lid Switch&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;hyprctl keyword monitor &#39;eDP-1, preferred, auto, 1&#39;&#34;</span><span class="p">)</span></span></span></code></pre></div><blockquote>
<p><strong>[需實機測試]</strong> Monitor 位置排列、HiDPI 縮放、VRR、旋轉、多螢幕熱插拔、合蓋行為。VM 通常只有一個虛擬螢幕。</p></blockquote>
<h3 id="dotfile-管理策略">Dotfile 管理策略</h3>
<p>Monitor 設定是典型的「機器專屬」配置。如果用 chezmoi，可以用 template 依機器名稱切換；如果用 stow，可以把 <code>monitors.lua</code> 排除在 Git 外、每台機器手動寫。</p>
<h2 id="輸入裝置配置">輸入裝置配置</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- ~/.config/hypr/hyprland.lua 或拆到獨立檔案</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">input</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="n">kb_layout</span> <span class="o">=</span> <span class="s2">&#34;us&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="c1">-- 多語系：kb_layout = &#34;us,de&#34;,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="c1">-- 切換：kb_options = &#34;grp:alt_shift_toggle&#34;,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="c1">-- Caps Lock 當 Escape：kb_options = &#34;caps:escape&#34;,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="n">kb_options</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="n">follow_mouse</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span>         <span class="c1">-- 焦點跟隨滑鼠</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">sensitivity</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>          <span class="c1">-- -1.0 到 1.0（0 = 不修改）</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="n">accel_profile</span> <span class="o">=</span> <span class="s2">&#34;flat&#34;</span><span class="p">,</span>   <span class="c1">-- flat 或 adaptive</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="n">repeat_rate</span> <span class="o">=</span> <span class="mi">25</span><span class="p">,</span>         <span class="c1">-- 長按時每秒重複幾次</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="n">repeat_delay</span> <span class="o">=</span> <span class="mi">600</span><span class="p">,</span>       <span class="c1">-- 按住多久開始重複（毫秒）</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="n">touchpad</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">            <span class="n">natural_scroll</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">            <span class="n">disable_while_typing</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">            <span class="p">[</span><span class="s2">&#34;tap-to-click&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">            <span class="n">drag_lock</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">            <span class="n">scroll_factor</span> <span class="o">=</span> <span class="mf">1.0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p>可用的 XKB options 查詢：<code>/usr/share/X11/xkb/rules/evdev.lst</code> 或 <code>localectl list-x11-keymap-options</code>。</p>
<blockquote>
<p><strong>[VM 可測試]</strong> 鍵盤 layout、repeat rate。<br>
<strong>[需實機測試]</strong> 觸控板設定、手勢、滑鼠靈敏度/加速曲線、多鍵盤配置。</p></blockquote>
<h2 id="keybind-設計">Keybind 設計</h2>
<h3 id="修飾鍵modifiersuper-是哪顆鍵">修飾鍵（Modifier）：<code>SUPER</code> 是哪顆鍵</h3>
<p>keybind 由「修飾鍵 + 按鍵」組成，Hyprland 配置裡最常當主修飾鍵的是 <code>SUPER</code>。<code>SUPER</code> 是 X11/Wayland 對 <strong>Meta 鍵</strong>的稱呼，在不同鍵盤上是不同的實體鍵：</p>
<ul>
<li><strong>PC 鍵盤</strong>：<strong>Windows 鍵</strong>（⊞，通常在 Ctrl 與 Alt 之間）。</li>
<li><strong>Mac 鍵盤</strong>：<strong>Command 鍵（⌘）</strong>。</li>
</ul>
<p>其他常見修飾鍵：<code>SHIFT</code>、<code>CTRL</code>（Control）、<code>ALT</code>（Mac 上是 Option ⌥）。組合寫法用空格串，例如 <code>&quot;SUPER SHIFT&quot;</code>。選 <code>SUPER</code> 當主修飾鍵是慣例——它幾乎不跟應用程式本身的快捷鍵衝突（應用多用 Ctrl / Alt）。</p>
<p>在 Mac 上跑虛擬機時，實體 ⌘ 會對應到 guest 的 <code>SUPER</code>，但 macOS 會先攔截部分 ⌘ 組合——這層 VM 專屬的坑見 <a href="../hyprland-vm-setup/">VM 環境設定</a>。</p>
<h3 id="bind-類型">Bind 類型</h3>
<p>Hyprland 提供多種 bind 函式，處理不同互動模式：</p>
<table>
  <thead>
      <tr>
          <th>函式</th>
          <th>行為</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>hl.bind()</code></td>
          <td>按一次觸發</td>
          <td>多數快捷鍵</td>
      </tr>
      <tr>
          <td><code>hl.binde()</code></td>
          <td>長按連續觸發</td>
          <td>調整視窗大小、音量</td>
      </tr>
      <tr>
          <td><code>hl.bindm()</code></td>
          <td>滑鼠綁定</td>
          <td>拖曳移動 / 調整視窗</td>
      </tr>
      <tr>
          <td><code>hl.bindr()</code></td>
          <td>放開時觸發</td>
          <td>切換模式</td>
      </tr>
      <tr>
          <td><code>hl.bindl()</code></td>
          <td>輸入鎖定也觸發</td>
          <td>合蓋開關、鎖屏時的媒體鍵</td>
      </tr>
      <tr>
          <td><code>hl.bindel()</code></td>
          <td>連續 + 鎖定</td>
          <td>鎖屏時的音量鍵（長按連續、鎖定可用）</td>
      </tr>
  </tbody>
</table>
<blockquote>
<p><strong>[VM 可測試]</strong> 所有 keybind 邏輯和配置語法。</p></blockquote>
<h3 id="基本操作">基本操作</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- ~/.config/hypr/keybinds.lua</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">-- 基本操作</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;Return&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;kitty&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;Q&#34;</span><span class="p">,</span> <span class="s2">&#34;killactive&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;D&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;wofi --show drun&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;F&#34;</span><span class="p">,</span> <span class="s2">&#34;fullscreen&#34;</span><span class="p">,</span> <span class="s2">&#34;0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;Space&#34;</span><span class="p">,</span> <span class="s2">&#34;togglefloating&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">-- 焦點移動（vim 風格）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;H&#34;</span><span class="p">,</span> <span class="s2">&#34;movefocus&#34;</span><span class="p">,</span> <span class="s2">&#34;l&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;J&#34;</span><span class="p">,</span> <span class="s2">&#34;movefocus&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;K&#34;</span><span class="p">,</span> <span class="s2">&#34;movefocus&#34;</span><span class="p">,</span> <span class="s2">&#34;u&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;L&#34;</span><span class="p">,</span> <span class="s2">&#34;movefocus&#34;</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">-- 視窗移動</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;H&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">,</span> <span class="s2">&#34;l&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;J&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">,</span> <span class="s2">&#34;d&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;K&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">,</span> <span class="s2">&#34;u&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;L&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1">-- 視窗大小調整（長按連續觸發）</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;SUPER CTRL&#34;</span><span class="p">,</span> <span class="s2">&#34;H&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;-20 0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;SUPER CTRL&#34;</span><span class="p">,</span> <span class="s2">&#34;J&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;0 20&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;SUPER CTRL&#34;</span><span class="p">,</span> <span class="s2">&#34;K&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;0 -20&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;SUPER CTRL&#34;</span><span class="p">,</span> <span class="s2">&#34;L&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;20 0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1">-- 滑鼠綁定：SUPER + 左鍵拖曳移動、右鍵拖曳調整大小</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="n">hl.bindm</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;mouse:272&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="n">hl.bindm</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;mouse:273&#34;</span><span class="p">,</span> <span class="s2">&#34;resizewindow&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c1">-- Workspace 切換</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;1&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;2&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;3&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;3&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;4&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;4&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;5&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;5&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;6&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;6&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;7&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;7&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;8&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;9&#34;</span><span class="p">,</span> <span class="s2">&#34;workspace&#34;</span><span class="p">,</span> <span class="s2">&#34;9&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1">-- 把視窗送到指定 workspace</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;1&#34;</span><span class="p">,</span> <span class="s2">&#34;movetoworkspace&#34;</span><span class="p">,</span> <span class="s2">&#34;1&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;2&#34;</span><span class="p">,</span> <span class="s2">&#34;movetoworkspace&#34;</span><span class="p">,</span> <span class="s2">&#34;2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;3&#34;</span><span class="p">,</span> <span class="s2">&#34;movetoworkspace&#34;</span><span class="p">,</span> <span class="s2">&#34;3&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="c1">-- 以此類推</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="c1">-- 螢幕截圖</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;Print&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;grimblast copy area&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;Print&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;grimblast copy output&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1">-- 多螢幕焦點切換</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;comma&#34;</span><span class="p">,</span> <span class="s2">&#34;focusmonitor&#34;</span><span class="p">,</span> <span class="s2">&#34;l&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;period&#34;</span><span class="p">,</span> <span class="s2">&#34;focusmonitor&#34;</span><span class="p">,</span> <span class="s2">&#34;r&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;comma&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">,</span> <span class="s2">&#34;mon:l&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER SHIFT&#34;</span><span class="p">,</span> <span class="s2">&#34;period&#34;</span><span class="p">,</span> <span class="s2">&#34;movewindow&#34;</span><span class="p">,</span> <span class="s2">&#34;mon:r&#34;</span><span class="p">)</span></span></span></code></pre></div><h3 id="設計原則">設計原則</h3>
<ul>
<li><strong>SUPER</strong>（Windows/Command 鍵）當 modifier，避免跟應用程式快捷鍵衝突</li>
<li><strong>方向操作</strong>統一用 vim 的 HJKL，降低記憶負擔</li>
<li><strong>modifier 分層</strong>：SUPER 是焦點、SUPER+SHIFT 是移動、SUPER+CTRL 是調整大小</li>
<li>常用操作（開 terminal、關視窗、切 workspace）放在最順手的位置</li>
</ul>
<h3 id="submap模態快捷鍵">Submap：模態快捷鍵</h3>
<p>Submap 類似 vim 的模式切換——進入某個 submap 後，快捷鍵的意義改變，直到離開。適合需要連續操作的場景（如調整大小）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- 按 SUPER+R 進入 resize 模式</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;R&#34;</span><span class="p">,</span> <span class="s2">&#34;submap&#34;</span><span class="p">,</span> <span class="s2">&#34;resize&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">-- 定義 resize 模式的快捷鍵</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">hl.submap</span><span class="p">(</span><span class="s2">&#34;resize&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;H&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;-20 0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;L&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;20 0&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;K&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;0 -20&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">hl.binde</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;J&#34;</span><span class="p">,</span> <span class="s2">&#34;resizeactive&#34;</span><span class="p">,</span> <span class="s2">&#34;0 20&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;escape&#34;</span><span class="p">,</span> <span class="s2">&#34;submap&#34;</span><span class="p">,</span> <span class="s2">&#34;reset&#34;</span><span class="p">)</span>   <span class="c1">-- Escape 離開</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">hl.submap</span><span class="p">(</span><span class="s2">&#34;reset&#34;</span><span class="p">)</span></span></span></code></pre></div><p>進入 resize 模式後，直接按 HJKL（不需要 modifier）就能持續調整大小，按 Escape 回到正常模式。</p>
<h3 id="媒體鍵與硬體鍵">媒體鍵與硬體鍵</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- 音量（鎖屏時也能用、長按連續觸發）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">hl.bindel</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioRaiseVolume&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">hl.bindel</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioLowerVolume&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioMute&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioMicMute&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">-- 亮度</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">hl.bindel</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86MonBrightnessUp&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;brightnessctl s 10%+&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">hl.bindel</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86MonBrightnessDown&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;brightnessctl s 10%-&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">-- 媒體播放</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioPlay&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;playerctl play-pause&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioNext&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;playerctl next&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="n">hl.bindl</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="s2">&#34;XF86AudioPrev&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;playerctl previous&#34;</span><span class="p">)</span></span></span></code></pre></div><blockquote>
<p><strong>[需實機測試]</strong> 媒體鍵（XF86Audio*）、亮度鍵（XF86MonBrightness*）、合蓋開關——這些依賴實體硬體送出正確的 keycode。VM 鍵盤通常沒有這些鍵。</p></blockquote>
<h2 id="環境變數">環境變數</h2>
<p>環境變數在 <code>hl.config()</code> 的 <code>env</code> 區段設定，影響 Hyprland 自身和其下執行的應用程式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- ~/.config/hypr/env.lua</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="c1">-- 游標主題</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="s2">&#34;XCURSOR_SIZE, 24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="s2">&#34;XCURSOR_THEME, Bibata-Modern-Classic&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="s2">&#34;HYPRCURSOR_SIZE, 24&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;HYPRCURSOR_THEME, Bibata-Modern-Classic&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="c1">-- Qt 應用程式</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="s2">&#34;QT_AUTO_SCREEN_SCALE_FACTOR, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="s2">&#34;QT_QPA_PLATFORM, wayland;xcb&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="s2">&#34;QT_QPA_PLATFORMTHEME, qt5ct&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="s2">&#34;QT_WAYLAND_DISABLE_WINDOWDECORATION, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="c1">-- GTK 應用程式</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="s2">&#34;GDK_BACKEND, wayland,x11,*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="c1">-- XDG session</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="s2">&#34;XDG_CURRENT_DESKTOP, Hyprland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">        <span class="s2">&#34;XDG_SESSION_TYPE, wayland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="s2">&#34;XDG_SESSION_DESKTOP, Hyprland&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="c1">-- Electron 應用程式（VS Code, Discord 等）</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="s2">&#34;ELECTRON_OZONE_PLATFORM_HINT, auto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="c1">-- Firefox</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="s2">&#34;MOZ_ENABLE_WAYLAND, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><h3 id="nvidia-專用環境變數">NVIDIA 專用環境變數</h3>
<p>如果使用 NVIDIA 顯卡，需要額外設定（AMD 和 Intel 不需要）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- 只在 NVIDIA 機器上加這些</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="s2">&#34;LIBVA_DRIVER_NAME, nvidia&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="s2">&#34;GBM_BACKEND, nvidia-drm&#34;</span><span class="p">,</span>             <span class="c1">-- Firefox 若崩潰就移除這行</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="s2">&#34;__GLX_VENDOR_LIBRARY_NAME, nvidia&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="s2">&#34;NVD_BACKEND, direct&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">cursor</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">no_hardware_cursors</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>             <span class="c1">-- NVIDIA 常見的游標問題修正</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="n">allow_dumb_copy</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><blockquote>
<p><strong>[VM 不適用]</strong> NVIDIA 環境變數和驅動設定在 VM 裡無意義——VM 使用 virtio-gpu 或軟體渲染。這些設定只在實體 NVIDIA 機器上測試。</p></blockquote>
<h3 id="vm-專用環境變數">VM 專用環境變數</h3>
<p>在 VM（UTM/QEMU）裡跑 Hyprland 需要額外的回退設定：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">-- 只在 VM 裡加，實機要移除</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="s2">&#34;WLR_RENDERER_ALLOW_SOFTWARE, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">        <span class="s2">&#34;LIBGL_ALWAYS_SOFTWARE, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">        <span class="s2">&#34;WLR_NO_HARDWARE_CURSORS, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><blockquote>
<p><strong>[已驗證]</strong> Hyprland 0.55（Aquamarine 渲染後端）+ UTM virtio-gpu-gl-pci 實測：使用 GPU 加速時不需要 <code>WLR_RENDERER</code> 或 <code>LIBGL_ALWAYS_SOFTWARE</code>，Hyprland 自動走 VirGL/Venus 硬體路徑。<code>WLR_NO_HARDWARE_CURSORS</code> 仍需要（virtio-gpu 不支援硬體 cursor）。完整 VM 設定見 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/" data-link-title="Hyprland VM 環境設定與測試矩陣" data-link-desc="要在 VM 裡測試 Hyprland 配置、或判斷某個設定該在 VM 還是實機驗證時回來讀">VM 環境設定與測試矩陣</a>。</p></blockquote>
]]></content:encoded></item><item><title>日誌判讀與診斷工具</title><link>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/log-reading-diagnostic-tools/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/log-reading-diagnostic-tools/</guid><description>&lt;p>恢復操作解決的是「怎麼讓桌面回來」，日誌判讀解決的是「為什麼會壞掉」。前者是急救，後者是找病因。如果同一個問題反覆出現，只做急救不找根因會一直繞圈。&lt;/p>
&lt;h2 id="journalctl系統日誌的主要入口">journalctl：系統日誌的主要入口&lt;/h2>
&lt;p>systemd 的日誌系統（journal）集中收錄所有 service、kernel、user session 的 log。&lt;code>journalctl&lt;/code> 是查詢這些日誌的指令。&lt;/p>
&lt;h3 id="基本用法">基本用法&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 本次開機的所有日誌&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">journalctl -b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 本次開機的錯誤以上等級（err + crit + alert + emerg）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">journalctl -b -p err
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 本次開機，只看最後 50 行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">journalctl -b -n &lt;span class="m">50&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 上一次開機的日誌（如果問題發生在上次開機、這次重開後想查）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">journalctl -b -1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># 即時跟蹤新 log（類似 tail -f）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">journalctl -f&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="過濾特定來源">過濾特定來源&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只看 Hyprland 相關&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">journalctl -b &lt;span class="p">|&lt;/span> grep -i hypr
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只看特定 systemd user unit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">journalctl --user -u waybar -b
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只看 kernel 訊息（等同 dmesg）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">journalctl -b -k
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只看某個 process 的 log（用 PID）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">journalctl &lt;span class="nv">_PID&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">12345&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="時間範圍過濾">時間範圍過濾&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 最近 10 分鐘的 log&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">journalctl --since &lt;span class="s2">&amp;#34;10 min ago&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 指定時間區間&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">journalctl --since &lt;span class="s2">&amp;#34;2026-06-30 14:00&amp;#34;&lt;/span> --until &lt;span class="s2">&amp;#34;2026-06-30 14:30&amp;#34;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="dmesgkernel-層訊息">dmesg：Kernel 層訊息&lt;/h2>
&lt;p>&lt;code>dmesg&lt;/code> 顯示 kernel ring buffer 的內容——硬體偵測、driver 載入、硬體錯誤這些 kernel 層面的事件。排查 GPU driver 問題、USB 裝置問題、磁碟錯誤時需要看這裡。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 所有 kernel 訊息（帶時間戳記）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">dmesg -T
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 只看錯誤和警告&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">dmesg -T --level&lt;span class="o">=&lt;/span>err,warn
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># GPU 相關（NVIDIA）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">dmesg -T &lt;span class="p">|&lt;/span> grep -i nvidia
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="c1"># GPU 相關（AMD）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">dmesg -T &lt;span class="p">|&lt;/span> grep -i amdgpu
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="c1"># USB 相關（鍵盤滑鼠突然不回應時看這裡）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">dmesg -T &lt;span class="p">|&lt;/span> grep -i usb&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>GPU driver 問題在 dmesg 裡的嚴重度差異很大：&lt;/p>
&lt;p>一般 GPU hang（driver 嘗試自動恢復）：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">[ 123.456] nvidia-modeset: ERROR: ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">[ 123.789] NVRM: Xid (PCI:0000:01:00): 79, pid=1234, ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">[ 124.000] amdgpu: GPU reset begin!
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">[ 124.500] amdgpu: GPU reset succeeded&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>NVIDIA 的 &lt;code>Xid&lt;/code> 錯誤代碼表示不同類型的 GPU 錯誤。常見的 &lt;code>Xid 79&lt;/code> 是 GPU fallback，&lt;code>Xid 31&lt;/code> 是 GPU setup failure。完整代碼表可在 NVIDIA 官方文件搜尋「Xid Errors」。&lt;/p></description><content:encoded><![CDATA[<p>恢復操作解決的是「怎麼讓桌面回來」，日誌判讀解決的是「為什麼會壞掉」。前者是急救，後者是找病因。如果同一個問題反覆出現，只做急救不找根因會一直繞圈。</p>
<h2 id="journalctl系統日誌的主要入口">journalctl：系統日誌的主要入口</h2>
<p>systemd 的日誌系統（journal）集中收錄所有 service、kernel、user session 的 log。<code>journalctl</code> 是查詢這些日誌的指令。</p>
<h3 id="基本用法">基本用法</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 本次開機的所有日誌</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">journalctl -b
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 本次開機的錯誤以上等級（err + crit + alert + emerg）</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">journalctl -b -p err
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 本次開機，只看最後 50 行</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">journalctl -b -n <span class="m">50</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 上一次開機的日誌（如果問題發生在上次開機、這次重開後想查）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">journalctl -b -1
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 即時跟蹤新 log（類似 tail -f）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">journalctl -f</span></span></code></pre></div><h3 id="過濾特定來源">過濾特定來源</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 只看 Hyprland 相關</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">journalctl -b <span class="p">|</span> grep -i hypr
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 只看特定 systemd user unit</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">journalctl --user -u waybar -b
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 只看 kernel 訊息（等同 dmesg）</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">journalctl -b -k
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 只看某個 process 的 log（用 PID）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">journalctl <span class="nv">_PID</span><span class="o">=</span><span class="m">12345</span></span></span></code></pre></div><h3 id="時間範圍過濾">時間範圍過濾</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 最近 10 分鐘的 log</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">journalctl --since <span class="s2">&#34;10 min ago&#34;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 指定時間區間</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">journalctl --since <span class="s2">&#34;2026-06-30 14:00&#34;</span> --until <span class="s2">&#34;2026-06-30 14:30&#34;</span></span></span></code></pre></div><h2 id="dmesgkernel-層訊息">dmesg：Kernel 層訊息</h2>
<p><code>dmesg</code> 顯示 kernel ring buffer 的內容——硬體偵測、driver 載入、硬體錯誤這些 kernel 層面的事件。排查 GPU driver 問題、USB 裝置問題、磁碟錯誤時需要看這裡。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 所有 kernel 訊息（帶時間戳記）</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">dmesg -T
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 只看錯誤和警告</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">dmesg -T --level<span class="o">=</span>err,warn
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># GPU 相關（NVIDIA）</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">dmesg -T <span class="p">|</span> grep -i nvidia
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># GPU 相關（AMD）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">dmesg -T <span class="p">|</span> grep -i amdgpu
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># USB 相關（鍵盤滑鼠突然不回應時看這裡）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">dmesg -T <span class="p">|</span> grep -i usb</span></span></code></pre></div><p>GPU driver 問題在 dmesg 裡的嚴重度差異很大：</p>
<p>一般 GPU hang（driver 嘗試自動恢復）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">[  123.456] nvidia-modeset: ERROR: ...
</span></span><span class="line"><span class="ln">2</span><span class="cl">[  123.789] NVRM: Xid (PCI:0000:01:00): 79, pid=1234, ...
</span></span><span class="line"><span class="ln">3</span><span class="cl">[  124.000] amdgpu: GPU reset begin!
</span></span><span class="line"><span class="ln">4</span><span class="cl">[  124.500] amdgpu: GPU reset succeeded</span></span></code></pre></div><p>NVIDIA 的 <code>Xid</code> 錯誤代碼表示不同類型的 GPU 錯誤。常見的 <code>Xid 79</code> 是 GPU fallback，<code>Xid 31</code> 是 GPU setup failure。完整代碼表可在 NVIDIA 官方文件搜尋「Xid Errors」。</p>
<p>硬體層級故障（嚴重，可能需要檢查硬體）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">[  123.789] NVRM: Xid (PCI:0000:01:00): 79, pid=1234, GPU has fallen off the bus</span></span></code></pre></div><p><code>GPU has fallen off the bus</code> 表示 GPU 跟主機板的 PCIe 連線完全中斷。偶發一次可能是 driver 問題，反覆出現通常是硬體故障（PCIe 供電不足、顯卡接觸不良、過熱）。</p>
<h2 id="hyprctlhyprland-的-runtime-狀態查詢">hyprctl：Hyprland 的 Runtime 狀態查詢</h2>
<p><code>hyprctl</code> 是 Hyprland 提供的命令列控制工具，可以在 compositor 運行中查詢狀態和執行操作。只有在 Hyprland 正在跑的時候才能使用。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 目前所有視窗的資訊</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">hyprctl clients
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 目前的 monitor 設定</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">hyprctl monitors
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 目前的 workspace 資訊</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">hyprctl workspaces
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># Hyprland 版本和 build 資訊</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">hyprctl version
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 重新載入 config（不重啟 compositor）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">hyprctl reload
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"># 查看上一次 config reload 是否有錯誤</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">hyprctl systeminfo</span></span></code></pre></div><p><code>hyprctl reload</code> 是測試 config 變更的安全方式。如果 config 有語法錯誤，reload 會報錯但 compositor 繼續用舊 config 跑，不會崩潰。</p>
<h2 id="systemctlservice-狀態管理">systemctl：Service 狀態管理</h2>
<p>桌面環境的工具（waybar、mako 等）如果用 systemd user unit 管理，可以用 <code>systemctl --user</code> 查看狀態和重啟。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 查看某個 user service 的狀態</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">systemctl --user status waybar
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 輸出範例：</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># waybar.service - Highly customizable Wayland bar</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">#    Loaded: loaded (/usr/lib/systemd/user/waybar.service; enabled)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">#    Active: active (running) since Mon 2026-06-30 10:00:00 CST</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">#    Main PID: 1234 (waybar)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 重啟</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">systemctl --user restart waybar
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 看最近的 log</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">systemctl --user status waybar -n <span class="m">20</span></span></span></code></pre></div><p>如果這些工具不是 systemd unit（在 Hyprland config 裡用 <code>exec-once</code> 啟動的），就不能用 <code>systemctl</code> 管理。改用 <code>pgrep</code> 和 <code>kill</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pgrep waybar      <span class="c1"># 查看是否在跑</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">killall waybar    <span class="c1"># 停止</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">waybar <span class="p">&amp;</span>          <span class="c1"># 背景啟動</span></span></span></code></pre></div><h2 id="即時資源監控">即時資源監控</h2>
<p>排查效能問題和記憶體耗盡時，需要看即時的系統資源使用情況。</p>
<p><strong>htop</strong>：互動式 process 監控。按 <code>M</code> 可以按記憶體用量排序，按 <code>P</code> 按 CPU 排序。找到佔用異常的 process 後按 <code>F9</code> 可以直接 kill。</p>
<p><strong>btop</strong>：功能更豐富的替代品，顯示 CPU、記憶體、磁碟、網路的即時使用情況，圖形化介面比 htop 直觀。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">sudo pacman -S btop    <span class="c1"># Arch</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">sudo apt install btop  <span class="c1"># Debian/Ubuntu</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># 執行</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">btop</span></span></code></pre></div><p><strong>nvidia-smi</strong>：NVIDIA GPU 的專屬監控工具。顯示 GPU 使用率、記憶體、溫度、跑在上面的 process。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 一次性查看</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">nvidia-smi
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 持續監控（每 2 秒更新）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">nvidia-smi -l <span class="m">2</span></span></span></code></pre></div><h2 id="常見-log-pattern-速查">常見 Log Pattern 速查</h2>
<table>
  <thead>
      <tr>
          <th>Pattern</th>
          <th>出處</th>
          <th>代表什麼</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>Out of memory: Killed process</code></td>
          <td>journalctl / dmesg</td>
          <td>OOM Killer 殺了某個 process</td>
          <td>檢查被殺的 process 名稱、設定 swap 或 systemd-oomd</td>
      </tr>
      <tr>
          <td><code>GPU has fallen off the bus</code></td>
          <td>dmesg</td>
          <td>NVIDIA GPU 完全失聯</td>
          <td>檢查 PCIe 供電、更新 driver、檢查硬體</td>
      </tr>
      <tr>
          <td><code>Xid ... pid=</code></td>
          <td>dmesg</td>
          <td>NVIDIA GPU 錯誤（Xid 編號對應不同類型的錯誤）</td>
          <td>查 NVIDIA 的 Xid 錯誤代碼表</td>
      </tr>
      <tr>
          <td><code>GPU reset begin</code></td>
          <td>dmesg</td>
          <td>AMD GPU driver 嘗試 reset GPU</td>
          <td>通常會自動恢復，頻繁出現代表 driver 或硬體問題</td>
      </tr>
      <tr>
          <td><code>segfault at</code></td>
          <td>journalctl</td>
          <td>某個 process segfault（記憶體存取違規）</td>
          <td>記下 process 名稱，搜尋該軟體的已知 bug</td>
      </tr>
      <tr>
          <td><code>Failed to start</code></td>
          <td>systemctl status</td>
          <td>systemd unit 啟動失敗</td>
          <td>看完整的 status 輸出和 journalctl log 找原因</td>
      </tr>
      <tr>
          <td><code>config error</code> / <code>parse error</code></td>
          <td>各工具自身的 log</td>
          <td>Config 檔語法錯誤</td>
          <td>檢查最近改過的 config 檔</td>
      </tr>
  </tbody>
</table>
<h2 id="排查流程">排查流程</h2>
<p>這篇是 Hyprland 桌面的具體日誌工具；背後「先讀權威狀態、不靠肉眼猜」的通用診斷心法（每種問題的權威來源、四步流程），見 <a href="/blog/linux/debug/diagnosis-read-authoritative-state/" data-link-title="診斷心法：讀權威狀態，不靠肉眼猜表象" data-link-desc="Linux 上一個現象看起來像 A 卻可能是 B、想建立一套先讀權威狀態再下判斷的除錯紀律、避免看畫面就猜而猜錯時回來讀">Linux 除錯與診斷：診斷心法</a>。遇到桌面環境問題時的判讀順序：</p>
<ol>
<li>
<p><strong>判斷影響範圍</strong>：只有一個視窗壞了、某個工具壞了、整個桌面壞了、還是系統完全不回應？影響範圍決定要看哪一層的 log。</p>
</li>
<li>
<p><strong>看 journalctl</strong>：<code>journalctl -b -p err</code> 先看本次開機有沒有錯誤等級的訊息。大部分 userspace 的問題（compositor crash、工具 crash）會出現在這裡。</p>
</li>
<li>
<p><strong>看 dmesg</strong>：如果 journalctl 沒有明顯線索、或症狀跟硬體有關（畫面凍結、USB 不回應），<code>dmesg -T --level=err,warn</code> 看 kernel 層有沒有硬體或 driver 錯誤。</p>
</li>
<li>
<p><strong>查特定工具的狀態</strong>：<code>systemctl --user status &lt;tool&gt;</code> 或 <code>pgrep &lt;tool&gt;</code> 確認工具是否還活著。如果死了，看它最後的 log 訊息。</p>
</li>
<li>
<p><strong>即時監控</strong>：如果問題是漸進式的（越來越慢、偶爾卡頓），開 <code>btop</code> 或 <code>htop</code> 觀察 CPU 和記憶體的即時趨勢，找出佔用異常的 process。</p>
</li>
</ol>
<h2 id="找到問題後的下一步">找到問題後的下一步</h2>
<p>判讀完 log 確認問題類型後，行動路徑依問題性質分流：</p>
<ul>
<li><strong>Config 錯誤</strong>：直接修 config，用 <code>hyprctl reload</code> 或重啟工具驗證。操作步驟見<a href="/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作</a>。</li>
<li><strong>軟體 bug</strong>（segfault、特定操作觸發 crash）：到該軟體的 issue tracker（通常在 GitHub）搜尋錯誤訊息。Hyprland 的 issue tracker 在 <code>github.com/hyprwm/Hyprland</code>。回報 bug 時附上 <code>hyprctl systeminfo</code> 的輸出和相關的 journalctl log。</li>
<li><strong>GPU driver 問題</strong>：NVIDIA 用戶檢查是否有更新的 driver 版本（<code>pacman -Syu nvidia</code>）。AMD 用戶的 driver 跟 kernel 綁定，更新 kernel 就更新 driver（<code>pacman -Syu linux</code>）。</li>
<li><strong>硬體故障</strong>（<code>GPU has fallen off the bus</code> 反覆出現）：軟體層面無法解決，需要檢查硬體（PCIe 插槽接觸、供電、溫度）。</li>
</ul>
]]></content:encoded></item><item><title>Caelestia 總覽：預組裝的 Hyprland 桌面 Shell</title><link>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-overview/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-overview/</guid><description>&lt;p>Caelestia 是基於 Quickshell 框架的 Hyprland 桌面 shell。它把狀態列、啟動器、通知、鎖屏、桌布管理這些在平鋪式桌面上需要各自挑選和配置的元件，整合成一套設計一致的成品。本質上是一套高品質的 dotfiles/rice，不是一個穩定的桌面環境產品。&lt;/p>
&lt;h2 id="caelestia-提供的元件">Caelestia 提供的元件&lt;/h2>
&lt;p>手動拼裝的桌面需要 6-8 個獨立工具各自配置。Caelestia 把這些元件用 Quickshell 統一實作：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>元件&lt;/th>
 &lt;th>手動拼裝的對應物&lt;/th>
 &lt;th>Caelestia 的實作&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>狀態列（Bar）&lt;/td>
 &lt;td>Waybar&lt;/td>
 &lt;td>工作區指示器、視窗標題、系統匣、時鐘&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>啟動器（Launcher）&lt;/td>
 &lt;td>Wofi / Rofi&lt;/td>
 &lt;td>模糊搜尋應用程式 + 桌布選擇介面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>通知&lt;/td>
 &lt;td>Mako / Dunst&lt;/td>
 &lt;td>分組通知、過期控制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>鎖屏&lt;/td>
 &lt;td>Hyprlock&lt;/td>
 &lt;td>模糊背景、可選指紋認證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>OSD&lt;/td>
 &lt;td>自己用 script 拼&lt;/td>
 &lt;td>音量 / 亮度變更的螢幕顯示&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>桌布管理&lt;/td>
 &lt;td>Hyprpaper / Swww&lt;/td>
 &lt;td>每螢幕桌布、從桌布動態取色（Material Design 3）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Dashboard&lt;/td>
 &lt;td>無對應（自己拼 widget）&lt;/td>
 &lt;td>媒體播放器（MPRIS）、CPU/GPU/RAM、天氣&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Sidebar&lt;/td>
 &lt;td>無對應&lt;/td>
 &lt;td>WiFi / 藍牙 / 暗色模式快速開關&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Session menu&lt;/td>
 &lt;td>自己用 script 拼&lt;/td>
 &lt;td>登出 / 關機 / 重啟&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Audio visualizer&lt;/td>
 &lt;td>Cava（終端機）&lt;/td>
 &lt;td>桌面上的音訊視覺化&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="quickshell-框架">Quickshell 框架&lt;/h2>
&lt;p>Quickshell 是一個 Qt6/QML 的 shell framework，用來在 Wayland compositor 上渲染 UI 元件。它的角色是 Caelestia 跟 Hyprland 之間的中介層：&lt;/p>
&lt;ul>
&lt;li>用 QML（Qt 的宣告式 UI 語言）描述介面元件&lt;/li>
&lt;li>透過 &lt;code>wlr-layer-shell&lt;/code> 協議把元件渲染成 Wayland layer surface（這是狀態列、啟動器能「黏在」螢幕邊緣的原因）&lt;/li>
&lt;li>直接存取 Hyprland IPC，可以即時讀取工作區狀態、視窗資訊&lt;/li>
&lt;li>配置檔修改後自動 reload，不需要重啟&lt;/li>
&lt;/ul>
&lt;p>跟其他桌面 shell 框架的差異主要在底層渲染引擎和配置語言。&lt;/p>
&lt;h2 id="跟-ags--eww-的比較">跟 AGS / Eww 的比較&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>框架&lt;/th>
 &lt;th>語言&lt;/th>
 &lt;th>渲染引擎&lt;/th>
 &lt;th>優勢&lt;/th>
 &lt;th>劣勢&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Quickshell（Caelestia 用）&lt;/td>
 &lt;td>QML（Qt6）&lt;/td>
 &lt;td>Qt&lt;/td>
 &lt;td>即時視窗預覽、Hyprland IPC、auto-reload、豐富動畫&lt;/td>
 &lt;td>Qt theming 複雜、positioning 不直覺、部分服務不完整&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>AGS / Astal&lt;/td>
 &lt;td>TypeScript / JS&lt;/td>
 &lt;td>GTK&lt;/td>
 &lt;td>完整的系統服務庫（Network、Bluetooth）、GObject、前端友善&lt;/td>
 &lt;td>GTK 在 Wayland layer surface 上的限制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Eww&lt;/td>
 &lt;td>Yuck（自定義）&lt;/td>
 &lt;td>GTK/Rust&lt;/td>
 &lt;td>輕量、配置語法簡單、生態成熟&lt;/td>
 &lt;td>功能不如前兩者豐富、Yuck 是自定義 DSL 要額外學&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>選型判讀：想要開箱即用的華麗桌面 → Caelestia（Quickshell）。想要自己一個個拼、有前端經驗 → AGS。想要最簡單輕量 → Eww。想要完全控制每個細節 → 手動拼裝 waybar + wofi + mako。&lt;/p>
&lt;h2 id="跟手動拼裝的-trade-off">跟手動拼裝的 Trade-off&lt;/h2>
&lt;p>&lt;strong>Caelestia 的優勢&lt;/strong>是省去「挑工具 + 各自配置 + 統一視覺風格」的大量前期功夫。Material Design 3 的動態取色（從桌布自動提取配色方案套用到所有元件）是手動拼裝很難做到的。&lt;/p>
&lt;p>&lt;strong>代價&lt;/strong>是兩個：&lt;/p>
&lt;p>第一，鎖定在 Caelestia 的設計決策裡。你可以調配色、改模組顯示、換桌布，但桌面的整體結構和行為邏輯是 Caelestia 決定的。想要「狀態列放底部 + 用不同的 launcher」這種結構性改動，比手動拼裝困難得多。&lt;/p>
&lt;p>第二，穩定性風險。Caelestia 的 README 明確寫：「Configs, and internal tokens used in them, may be changed or removed without notice」。這表示一次更新可能靜默破壞你的自訂設定。這跟 Hyprland 本身的 breaking changes 疊加，等於你的桌面有兩層快速移動的依賴。&lt;/p></description><content:encoded><![CDATA[<p>Caelestia 是基於 Quickshell 框架的 Hyprland 桌面 shell。它把狀態列、啟動器、通知、鎖屏、桌布管理這些在平鋪式桌面上需要各自挑選和配置的元件，整合成一套設計一致的成品。本質上是一套高品質的 dotfiles/rice，不是一個穩定的桌面環境產品。</p>
<h2 id="caelestia-提供的元件">Caelestia 提供的元件</h2>
<p>手動拼裝的桌面需要 6-8 個獨立工具各自配置。Caelestia 把這些元件用 Quickshell 統一實作：</p>
<table>
  <thead>
      <tr>
          <th>元件</th>
          <th>手動拼裝的對應物</th>
          <th>Caelestia 的實作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>狀態列（Bar）</td>
          <td>Waybar</td>
          <td>工作區指示器、視窗標題、系統匣、時鐘</td>
      </tr>
      <tr>
          <td>啟動器（Launcher）</td>
          <td>Wofi / Rofi</td>
          <td>模糊搜尋應用程式 + 桌布選擇介面</td>
      </tr>
      <tr>
          <td>通知</td>
          <td>Mako / Dunst</td>
          <td>分組通知、過期控制</td>
      </tr>
      <tr>
          <td>鎖屏</td>
          <td>Hyprlock</td>
          <td>模糊背景、可選指紋認證</td>
      </tr>
      <tr>
          <td>OSD</td>
          <td>自己用 script 拼</td>
          <td>音量 / 亮度變更的螢幕顯示</td>
      </tr>
      <tr>
          <td>桌布管理</td>
          <td>Hyprpaper / Swww</td>
          <td>每螢幕桌布、從桌布動態取色（Material Design 3）</td>
      </tr>
      <tr>
          <td>Dashboard</td>
          <td>無對應（自己拼 widget）</td>
          <td>媒體播放器（MPRIS）、CPU/GPU/RAM、天氣</td>
      </tr>
      <tr>
          <td>Sidebar</td>
          <td>無對應</td>
          <td>WiFi / 藍牙 / 暗色模式快速開關</td>
      </tr>
      <tr>
          <td>Session menu</td>
          <td>自己用 script 拼</td>
          <td>登出 / 關機 / 重啟</td>
      </tr>
      <tr>
          <td>Audio visualizer</td>
          <td>Cava（終端機）</td>
          <td>桌面上的音訊視覺化</td>
      </tr>
  </tbody>
</table>
<h2 id="quickshell-框架">Quickshell 框架</h2>
<p>Quickshell 是一個 Qt6/QML 的 shell framework，用來在 Wayland compositor 上渲染 UI 元件。它的角色是 Caelestia 跟 Hyprland 之間的中介層：</p>
<ul>
<li>用 QML（Qt 的宣告式 UI 語言）描述介面元件</li>
<li>透過 <code>wlr-layer-shell</code> 協議把元件渲染成 Wayland layer surface（這是狀態列、啟動器能「黏在」螢幕邊緣的原因）</li>
<li>直接存取 Hyprland IPC，可以即時讀取工作區狀態、視窗資訊</li>
<li>配置檔修改後自動 reload，不需要重啟</li>
</ul>
<p>跟其他桌面 shell 框架的差異主要在底層渲染引擎和配置語言。</p>
<h2 id="跟-ags--eww-的比較">跟 AGS / Eww 的比較</h2>
<table>
  <thead>
      <tr>
          <th>框架</th>
          <th>語言</th>
          <th>渲染引擎</th>
          <th>優勢</th>
          <th>劣勢</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Quickshell（Caelestia 用）</td>
          <td>QML（Qt6）</td>
          <td>Qt</td>
          <td>即時視窗預覽、Hyprland IPC、auto-reload、豐富動畫</td>
          <td>Qt theming 複雜、positioning 不直覺、部分服務不完整</td>
      </tr>
      <tr>
          <td>AGS / Astal</td>
          <td>TypeScript / JS</td>
          <td>GTK</td>
          <td>完整的系統服務庫（Network、Bluetooth）、GObject、前端友善</td>
          <td>GTK 在 Wayland layer surface 上的限制</td>
      </tr>
      <tr>
          <td>Eww</td>
          <td>Yuck（自定義）</td>
          <td>GTK/Rust</td>
          <td>輕量、配置語法簡單、生態成熟</td>
          <td>功能不如前兩者豐富、Yuck 是自定義 DSL 要額外學</td>
      </tr>
  </tbody>
</table>
<p>選型判讀：想要開箱即用的華麗桌面 → Caelestia（Quickshell）。想要自己一個個拼、有前端經驗 → AGS。想要最簡單輕量 → Eww。想要完全控制每個細節 → 手動拼裝 waybar + wofi + mako。</p>
<h2 id="跟手動拼裝的-trade-off">跟手動拼裝的 Trade-off</h2>
<p><strong>Caelestia 的優勢</strong>是省去「挑工具 + 各自配置 + 統一視覺風格」的大量前期功夫。Material Design 3 的動態取色（從桌布自動提取配色方案套用到所有元件）是手動拼裝很難做到的。</p>
<p><strong>代價</strong>是兩個：</p>
<p>第一，鎖定在 Caelestia 的設計決策裡。你可以調配色、改模組顯示、換桌布，但桌面的整體結構和行為邏輯是 Caelestia 決定的。想要「狀態列放底部 + 用不同的 launcher」這種結構性改動，比手動拼裝困難得多。</p>
<p>第二，穩定性風險。Caelestia 的 README 明確寫：「Configs, and internal tokens used in them, may be changed or removed without notice」。這表示一次更新可能靜默破壞你的自訂設定。這跟 Hyprland 本身的 breaking changes 疊加，等於你的桌面有兩層快速移動的依賴。</p>
<h2 id="三個-github-repo-的分工">三個 GitHub Repo 的分工</h2>
<table>
  <thead>
      <tr>
          <th>Repo</th>
          <th>角色</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>caelestia-dots/caelestia</code></td>
          <td>主 dotfiles — Hyprland config、應用程式配置</td>
      </tr>
      <tr>
          <td><code>caelestia-dots/shell</code></td>
          <td>UI 層 — Quickshell/QML 實作的所有桌面元件</td>
      </tr>
      <tr>
          <td><code>caelestia-dots/cli</code></td>
          <td>CLI 工具 — 安裝、主題切換、截圖、錄影等指令</td>
      </tr>
  </tbody>
</table>
<p>完整安裝（<code>caelestia install</code>）會同時部署三者。也可以只裝 shell（<code>caelestia-shell</code> AUR package），保留自己的 Hyprland config 和應用程式設定。</p>
<h2 id="定位認知">定位認知</h2>
<p>Caelestia 的本質是一套社群維護的 rice dotfiles，不是有 release cycle 和 backward compatibility 承諾的軟體產品。用它的心態應該接近「fork 別人的 dotfiles 來改」而不是「安裝一個桌面環境」。這個定位決定了它適合什麼人：享受折騰和客製化的人會從中得到很好的起點，想要穩定日用的人應該考慮 GNOME 或 KDE Plasma。</p>
]]></content:encoded></item><item><title>Linux Tiling WM 生態</title><link>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/linux-tiling-wm/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/linux-tiling-wm/</guid><description>&lt;p>Linux 桌面的視窗管理跑在**顯示協議（display protocol）**上，目前有兩套。&lt;/p>
&lt;p>&lt;strong>X11&lt;/strong>（X Window System）是用了三十多年的傳統協議。成熟、穩定、所有工具都支援。設計上的根本問題是安全性——任何 X11 應用程式都能讀取其他視窗的內容和鍵盤輸入，這是協議層的限制，不是 bug。&lt;/p>
&lt;p>&lt;strong>Wayland&lt;/strong> 是設計來取代 X11 的新協議。每個應用程式只看得到自己的視窗、效能更好、對現代硬體的支援更完整。多數主流發行版已經把 Wayland 設為預設，但部分老舊應用程式和特殊需求（某些螢幕錄製、遠端桌面工具）的支援還在追趕中，這些情境會透過 XWayland（相容層）跑 X11 應用程式。&lt;/p>
&lt;p>新的 tiling WM 主要基於 Wayland 開發，X11 上的老牌（i3, bspwm, dwm）仍然活躍但不再是未來方向。&lt;/p>
&lt;h2 id="主流-tiling-wm">主流 Tiling WM&lt;/h2>
&lt;p>&lt;strong>i3（X11）/ sway（Wayland）&lt;/strong> 是社群最大、文件最齊全、行為最可預測的選擇。i3 跑在 X11 上，sway 是它的 Wayland 移植，配置格式幾乎相同。配置檔用自己的語法，直覺的 &lt;code>key = action&lt;/code> 對應。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl"># sway/config 片段
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">set $mod Mod4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">bindsym $mod+h focus left
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">bindsym $mod+j focus down
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">bindsym $mod+k focus up
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">bindsym $mod+l focus right
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">bindsym $mod+Shift+h move left
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">bindsym $mod+1 workspace number 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">bindsym $mod+2 workspace number 2&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>穩定性是 i3/sway 最大的賣點——配置寫好之後很少因為更新而壞掉。適合想要可靠的平鋪工作流、不需要華麗視覺效果的人。&lt;/p>
&lt;p>&lt;strong>Hyprland（Wayland）&lt;/strong> 吸引的是在意桌面視覺品質的平鋪使用者——看 r/unixporn 的 Hyprland 截圖就知道，流暢的視窗切換動畫、圓角、視窗模糊、漸層邊框，這些傳統 tiling WM 社群視為不必要的裝飾，Hyprland 做成內建功能並且認真打磨。配置檔改完即時生效（hot reload），迭代調教的回饋循環很短。&lt;/p>
&lt;p>這份視覺投入換來的 trade-off 是穩定性。開發節奏快意味著偶爾會有 breaking changes——某次更新後配置語法改了（2026 年 4 月的 Lua 遷移就是一例）、某個選項改名或移除。把它當主力桌面，要有「更新後可能要回來調配置」的心理準備。&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">Hyprland 配置&lt;/a>會詳細講它的設定。&lt;/p>
&lt;p>&lt;strong>bspwm（X11）&lt;/strong> 是純粹的 BSP 樹狀分割。它只做一件事：管理視窗的樹狀結構。所有操作透過 &lt;code>bspc&lt;/code> 命令列工具驅動，快捷鍵綁定交給 sxhkd（一個獨立的快捷鍵 daemon）。UNIX 哲學——每個工具只做一件事，組合起來用。&lt;/p>
&lt;p>&lt;strong>dwm（X11）&lt;/strong> 極簡到配置要改 C 原始碼然後重新編譯。不是給「想要方便配置」的人用的，而是給「想要完全理解自己桌面每一行程式碼」的人用的。&lt;/p>
&lt;h2 id="拼裝式桌面的代價">拼裝式桌面的代價&lt;/h2>
&lt;p>Linux tiling WM 的桌面是你自己拼出來的：compositor（Hyprland/sway）負責視窗管理、狀態列（waybar）負責頂部資訊條、啟動器（rofi/wofi）負責 app 搜尋啟動、通知（mako/dunst）負責通知彈窗、鎖屏另外裝。每一塊是不同專案、不同作者。&lt;/p>
&lt;p>好處是每一塊都可以換。壞處是當某件事壞了，你要自己判斷是哪一層的問題。藍牙選單不能點？可能是狀態列的 module 設定錯、可能是 blueman 沒跑、可能是 D-Bus session 有問題。完整桌面環境（KDE/GNOME）幫你整合測試過了，拼裝式桌面沒有這層保障。&lt;/p>
&lt;h2 id="多螢幕的處理">多螢幕的處理&lt;/h2>
&lt;p>多螢幕是自動平鋪比較能展現價值的場景。每個螢幕是獨立的平鋪區域，視窗在哪個螢幕就在那個螢幕的範圍內平鋪。&lt;/p>
&lt;p>快捷鍵跨螢幕操作是標配功能：把焦點跳到另一個螢幕、把當前視窗丟到另一個螢幕（丟過去後自動融入那邊的平鋪佈局）。三螢幕時這個便利性比單螢幕更明顯——純滑鼠拖視窗跨螢幕需要的移動距離很長。&lt;/p>
&lt;p>工作區跟螢幕的綁定方式是各工具差異最大的地方。&lt;/p>
&lt;p>macOS 的 Spaces 是綁定螢幕的——每個螢幕有自己獨立的一組 Spaces。yabai 沿用這個行為，切換工作區時只影響當前螢幕。AeroSpace 用自己的虛擬工作區繞過 macOS 原生 Spaces 的限制，多螢幕操作被普遍認為更穩定、更直覺。&lt;/p>
&lt;p>Hyprland 的 workspace 可以動態指派到不同螢幕——workspace 3 現在在螢幕 A，你可以把它移到螢幕 B。這種模型彈性最大，但也需要清楚的心智模型來管理「哪個 workspace 在哪」。&lt;/p>
&lt;p>熱插拔螢幕（接上、拔掉外接螢幕）是各工具的常見痛點。拔掉螢幕時，該螢幕上的視窗要搬到剩餘螢幕重新平鋪；插上螢幕時，佈局要擴展。多數工具有對應設定（記住螢幕配置、自動還原佈局），但體驗不見得完美，偶爾需要手動整理。&lt;/p>
&lt;h2 id="dotfile-中的視窗管理配置">Dotfile 中的視窗管理配置&lt;/h2>
&lt;p>視窗管理是 dotfile 管理裡「桌面層」的核心。WM 配置檔決定了整個桌面的操作邏輯——快捷鍵怎麼綁、視窗間距多大、哪些 app 要浮動、工作區怎麼分配。&lt;/p>
&lt;p>macOS 工具的配置檔通常是一個檔案：AeroSpace 的 &lt;code>~/.aerospace.toml&lt;/code>、yabai 的 &lt;code>~/.yabairc&lt;/code> + &lt;code>~/.skhdrc&lt;/code>、Amethyst 的 &lt;code>~/.amethyst.yml&lt;/code>。把這些檔案放進 dotfile repo，換 Mac 時就能還原整套視窗管理行為。&lt;/p>
&lt;p>Linux tiling WM 的配置在 &lt;code>~/.config/&lt;/code> 下，通常是一個資料夾：Hyprland 的 &lt;code>~/.config/hypr/&lt;/code>、sway 的 &lt;code>~/.config/sway/&lt;/code>、i3 的 &lt;code>~/.config/i3/&lt;/code>。除了 WM 本身，狀態列（waybar 的 &lt;code>~/.config/waybar/&lt;/code>）、啟動器（rofi 的 &lt;code>~/.config/rofi/&lt;/code>）、通知（mako 的 &lt;code>~/.config/mako/&lt;/code>）等周邊元件的配置也各自有檔案。一套完整的 Linux 平鋪桌面，dotfile repo 裡可能會有十幾個配置目錄——這也是為什麼 Linux 桌面客製化社群那麼依賴 dotfile 管理工具（見&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">管理工具與目錄結構&lt;/a>）。&lt;/p></description><content:encoded><![CDATA[<p>Linux 桌面的視窗管理跑在**顯示協議（display protocol）**上，目前有兩套。</p>
<p><strong>X11</strong>（X Window System）是用了三十多年的傳統協議。成熟、穩定、所有工具都支援。設計上的根本問題是安全性——任何 X11 應用程式都能讀取其他視窗的內容和鍵盤輸入，這是協議層的限制，不是 bug。</p>
<p><strong>Wayland</strong> 是設計來取代 X11 的新協議。每個應用程式只看得到自己的視窗、效能更好、對現代硬體的支援更完整。多數主流發行版已經把 Wayland 設為預設，但部分老舊應用程式和特殊需求（某些螢幕錄製、遠端桌面工具）的支援還在追趕中，這些情境會透過 XWayland（相容層）跑 X11 應用程式。</p>
<p>新的 tiling WM 主要基於 Wayland 開發，X11 上的老牌（i3, bspwm, dwm）仍然活躍但不再是未來方向。</p>
<h2 id="主流-tiling-wm">主流 Tiling WM</h2>
<p><strong>i3（X11）/ sway（Wayland）</strong> 是社群最大、文件最齊全、行為最可預測的選擇。i3 跑在 X11 上，sway 是它的 Wayland 移植，配置格式幾乎相同。配置檔用自己的語法，直覺的 <code>key = action</code> 對應。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl"># sway/config 片段
</span></span><span class="line"><span class="ln">2</span><span class="cl">set $mod Mod4
</span></span><span class="line"><span class="ln">3</span><span class="cl">bindsym $mod+h focus left
</span></span><span class="line"><span class="ln">4</span><span class="cl">bindsym $mod+j focus down
</span></span><span class="line"><span class="ln">5</span><span class="cl">bindsym $mod+k focus up
</span></span><span class="line"><span class="ln">6</span><span class="cl">bindsym $mod+l focus right
</span></span><span class="line"><span class="ln">7</span><span class="cl">bindsym $mod+Shift+h move left
</span></span><span class="line"><span class="ln">8</span><span class="cl">bindsym $mod+1 workspace number 1
</span></span><span class="line"><span class="ln">9</span><span class="cl">bindsym $mod+2 workspace number 2</span></span></code></pre></div><p>穩定性是 i3/sway 最大的賣點——配置寫好之後很少因為更新而壞掉。適合想要可靠的平鋪工作流、不需要華麗視覺效果的人。</p>
<p><strong>Hyprland（Wayland）</strong> 吸引的是在意桌面視覺品質的平鋪使用者——看 r/unixporn 的 Hyprland 截圖就知道，流暢的視窗切換動畫、圓角、視窗模糊、漸層邊框，這些傳統 tiling WM 社群視為不必要的裝飾，Hyprland 做成內建功能並且認真打磨。配置檔改完即時生效（hot reload），迭代調教的回饋循環很短。</p>
<p>這份視覺投入換來的 trade-off 是穩定性。開發節奏快意味著偶爾會有 breaking changes——某次更新後配置語法改了（2026 年 4 月的 Lua 遷移就是一例）、某個選項改名或移除。把它當主力桌面，要有「更新後可能要回來調配置」的心理準備。<a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">Hyprland 配置</a>會詳細講它的設定。</p>
<p><strong>bspwm（X11）</strong> 是純粹的 BSP 樹狀分割。它只做一件事：管理視窗的樹狀結構。所有操作透過 <code>bspc</code> 命令列工具驅動，快捷鍵綁定交給 sxhkd（一個獨立的快捷鍵 daemon）。UNIX 哲學——每個工具只做一件事，組合起來用。</p>
<p><strong>dwm（X11）</strong> 極簡到配置要改 C 原始碼然後重新編譯。不是給「想要方便配置」的人用的，而是給「想要完全理解自己桌面每一行程式碼」的人用的。</p>
<h2 id="拼裝式桌面的代價">拼裝式桌面的代價</h2>
<p>Linux tiling WM 的桌面是你自己拼出來的：compositor（Hyprland/sway）負責視窗管理、狀態列（waybar）負責頂部資訊條、啟動器（rofi/wofi）負責 app 搜尋啟動、通知（mako/dunst）負責通知彈窗、鎖屏另外裝。每一塊是不同專案、不同作者。</p>
<p>好處是每一塊都可以換。壞處是當某件事壞了，你要自己判斷是哪一層的問題。藍牙選單不能點？可能是狀態列的 module 設定錯、可能是 blueman 沒跑、可能是 D-Bus session 有問題。完整桌面環境（KDE/GNOME）幫你整合測試過了，拼裝式桌面沒有這層保障。</p>
<h2 id="多螢幕的處理">多螢幕的處理</h2>
<p>多螢幕是自動平鋪比較能展現價值的場景。每個螢幕是獨立的平鋪區域，視窗在哪個螢幕就在那個螢幕的範圍內平鋪。</p>
<p>快捷鍵跨螢幕操作是標配功能：把焦點跳到另一個螢幕、把當前視窗丟到另一個螢幕（丟過去後自動融入那邊的平鋪佈局）。三螢幕時這個便利性比單螢幕更明顯——純滑鼠拖視窗跨螢幕需要的移動距離很長。</p>
<p>工作區跟螢幕的綁定方式是各工具差異最大的地方。</p>
<p>macOS 的 Spaces 是綁定螢幕的——每個螢幕有自己獨立的一組 Spaces。yabai 沿用這個行為，切換工作區時只影響當前螢幕。AeroSpace 用自己的虛擬工作區繞過 macOS 原生 Spaces 的限制，多螢幕操作被普遍認為更穩定、更直覺。</p>
<p>Hyprland 的 workspace 可以動態指派到不同螢幕——workspace 3 現在在螢幕 A，你可以把它移到螢幕 B。這種模型彈性最大，但也需要清楚的心智模型來管理「哪個 workspace 在哪」。</p>
<p>熱插拔螢幕（接上、拔掉外接螢幕）是各工具的常見痛點。拔掉螢幕時，該螢幕上的視窗要搬到剩餘螢幕重新平鋪；插上螢幕時，佈局要擴展。多數工具有對應設定（記住螢幕配置、自動還原佈局），但體驗不見得完美，偶爾需要手動整理。</p>
<h2 id="dotfile-中的視窗管理配置">Dotfile 中的視窗管理配置</h2>
<p>視窗管理是 dotfile 管理裡「桌面層」的核心。WM 配置檔決定了整個桌面的操作邏輯——快捷鍵怎麼綁、視窗間距多大、哪些 app 要浮動、工作區怎麼分配。</p>
<p>macOS 工具的配置檔通常是一個檔案：AeroSpace 的 <code>~/.aerospace.toml</code>、yabai 的 <code>~/.yabairc</code> + <code>~/.skhdrc</code>、Amethyst 的 <code>~/.amethyst.yml</code>。把這些檔案放進 dotfile repo，換 Mac 時就能還原整套視窗管理行為。</p>
<p>Linux tiling WM 的配置在 <code>~/.config/</code> 下，通常是一個資料夾：Hyprland 的 <code>~/.config/hypr/</code>、sway 的 <code>~/.config/sway/</code>、i3 的 <code>~/.config/i3/</code>。除了 WM 本身，狀態列（waybar 的 <code>~/.config/waybar/</code>）、啟動器（rofi 的 <code>~/.config/rofi/</code>）、通知（mako 的 <code>~/.config/mako/</code>）等周邊元件的配置也各自有檔案。一套完整的 Linux 平鋪桌面，dotfile repo 裡可能會有十幾個配置目錄——這也是為什麼 Linux 桌面客製化社群那麼依賴 dotfile 管理工具（見<a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">管理工具與目錄結構</a>）。</p>
<p>跟螢幕硬體綁定的設定（螢幕解析度、縮放比、螢幕排列順序）通常也寫在 WM 配置裡。這部分在跨機器搬移 dotfile 時需要調整——同一份 Hyprland 配置裡的 <code>monitor</code> 設定，在筆電上是一個螢幕、在桌機上可能是三個。常見做法是把硬體相關設定拆到單獨檔案（如 <code>monitors.lua</code>），主配置用 <code>require</code> 引入，這樣跨機器時只需要替換這一個檔案。<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步與環境重建</a>會講跨機器搬移時怎麼處理這類硬體差異。</p>
]]></content:encoded></item><item><title>Workspace、Window Rules 與外觀</title><link>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/workspace-rules-appearance/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/workspace-rules-appearance/</guid><description>&lt;p>本篇的配置範例使用 Lua 語法（Hyprland v0.55+）。Lua 基礎見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/lua-scripting-language/" data-link-title="Lua 腳本語言" data-link-desc="在 Hyprland 或 Neovim 配置檔遇到 Lua 語法看不懂時回來讀 — 配置檔需要的最小 Lua 知識">Lua 腳本語言&lt;/a>。&lt;/p>
&lt;h2 id="workspace-設定">Workspace 設定&lt;/h2>
&lt;p>Workspace 是平鋪式桌面的空間管理單位。Hyprland 的 workspace 是動態的——不需要預先定義多少個，按了不存在的 workspace 編號就會自動建立。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">-- 把特定 workspace 綁定到特定螢幕&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="n">hl.config&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="n">workspace&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;1, monitor:DP-1, default:true&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;2, monitor:DP-1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;3, monitor:DP-1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;7, monitor:HDMI-A-1, default:true&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;8, monitor:HDMI-A-1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;9, monitor:HDMI-A-1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="p">})&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>常見的使用模式：&lt;/p>
&lt;ul>
&lt;li>Workspace 1-3 放在主螢幕（寫程式用）&lt;/li>
&lt;li>Workspace 7-9 放在副螢幕（瀏覽器、通訊軟體、監控）&lt;/li>
&lt;li>用鍵盤瞬間切換（SUPER + 數字鍵），比 alt-tab 在一堆視窗裡找快&lt;/li>
&lt;/ul>
&lt;h3 id="per-workspace-layout-覆寫">Per-workspace layout 覆寫&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="n">hl.config&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="n">workspace&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;1, layoutopt:orientation:left&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">-- workspace 1 用左側 master&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;2, layoutopt:orientation:top&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">-- workspace 2 用頂部 master&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="p">})&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>[VM 可測試]&lt;/strong> Workspace 切換、綁定邏輯、per-workspace layout。&lt;br>
&lt;strong>[需實機測試]&lt;/strong> Workspace 綁定到特定實體螢幕的行為。&lt;/p>&lt;/blockquote>
&lt;h2 id="window-rules">Window Rules&lt;/h2>
&lt;p>Window rules 讓特定應用程式在開啟時自動套用設定——指定 workspace、強制浮動、設定大小等。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-lua" data-lang="lua">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="n">hl.config&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="n">windowrule&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 特定 app 自動送到指定 workspace&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;workspace 8, class:^(firefox)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;workspace 9, class:^(Slack)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 設定面板、對話框等維持浮動（不進平鋪）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;float, class:^(pavucontrol)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;float, class:^(nm-connection-editor)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;float, title:^(Open File)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;float, title:^(Save As)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- Picture-in-picture 浮動 + 置頂 + 固定大小&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;float, title:^(Picture-in-Picture)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;pin, title:^(Picture-in-Picture)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;size 480 270, title:^(Picture-in-Picture)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;move 100%-490 50, title:^(Picture-in-Picture)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 視窗透明度（active inactive）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;opacity 0.9, class:^(kitty)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;opacity 0.85 0.75, class:^(Code)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- 防止 Electron app 自動最大化&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;suppressevent maximize, class:.*&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl"> &lt;span class="c1">-- XWayland video bridge（螢幕分享用）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;opacity 0.0 override, class:^(xwaylandvideobridge)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;noanim, class:^(xwaylandvideobridge)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;noinitialfocus, class:^(xwaylandvideobridge)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;maxsize 1 1, class:^(xwaylandvideobridge)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">31&lt;/span>&lt;span class="cl"> &lt;span class="s2">&amp;#34;noblur, class:^(xwaylandvideobridge)$&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">32&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">33&lt;/span>&lt;span class="cl">&lt;span class="p">})&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="查詢應用程式的-class-和-title">查詢應用程式的 class 和 title&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">hyprctl clients &lt;span class="c1"># 列出所有開啟的視窗（含 class、title、PID 等）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">hyprctl activewindow &lt;span class="c1"># 當前焦點視窗的資訊&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Window rules 用 regex 匹配 &lt;code>class&lt;/code> 和 &lt;code>title&lt;/code>。靜態 rules 匹配的是視窗開啟時的初始值（&lt;code>initialTitle&lt;/code> / &lt;code>initialClass&lt;/code>）。&lt;/p></description><content:encoded><![CDATA[<p>本篇的配置範例使用 Lua 語法（Hyprland v0.55+）。Lua 基礎見 <a href="/blog/linux/dotfile/knowledge-cards/lua-scripting-language/" data-link-title="Lua 腳本語言" data-link-desc="在 Hyprland 或 Neovim 配置檔遇到 Lua 語法看不懂時回來讀 — 配置檔需要的最小 Lua 知識">Lua 腳本語言</a>。</p>
<h2 id="workspace-設定">Workspace 設定</h2>
<p>Workspace 是平鋪式桌面的空間管理單位。Hyprland 的 workspace 是動態的——不需要預先定義多少個，按了不存在的 workspace 編號就會自動建立。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- 把特定 workspace 綁定到特定螢幕</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">workspace</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="s2">&#34;1, monitor:DP-1, default:true&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="s2">&#34;2, monitor:DP-1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="s2">&#34;3, monitor:DP-1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="s2">&#34;7, monitor:HDMI-A-1, default:true&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="s2">&#34;8, monitor:HDMI-A-1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;9, monitor:HDMI-A-1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p>常見的使用模式：</p>
<ul>
<li>Workspace 1-3 放在主螢幕（寫程式用）</li>
<li>Workspace 7-9 放在副螢幕（瀏覽器、通訊軟體、監控）</li>
<li>用鍵盤瞬間切換（SUPER + 數字鍵），比 alt-tab 在一堆視窗裡找快</li>
</ul>
<h3 id="per-workspace-layout-覆寫">Per-workspace layout 覆寫</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">workspace</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="s2">&#34;1, layoutopt:orientation:left&#34;</span><span class="p">,</span>     <span class="c1">-- workspace 1 用左側 master</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="s2">&#34;2, layoutopt:orientation:top&#34;</span><span class="p">,</span>      <span class="c1">-- workspace 2 用頂部 master</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><blockquote>
<p><strong>[VM 可測試]</strong> Workspace 切換、綁定邏輯、per-workspace layout。<br>
<strong>[需實機測試]</strong> Workspace 綁定到特定實體螢幕的行為。</p></blockquote>
<h2 id="window-rules">Window Rules</h2>
<p>Window rules 讓特定應用程式在開啟時自動套用設定——指定 workspace、強制浮動、設定大小等。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">windowrule</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="c1">-- 特定 app 自動送到指定 workspace</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="s2">&#34;workspace 8, class:^(firefox)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="s2">&#34;workspace 9, class:^(Slack)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="c1">-- 設定面板、對話框等維持浮動（不進平鋪）</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="s2">&#34;float, class:^(pavucontrol)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;float, class:^(nm-connection-editor)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="s2">&#34;float, title:^(Open File)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="s2">&#34;float, title:^(Save As)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="c1">-- Picture-in-picture 浮動 + 置頂 + 固定大小</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="s2">&#34;float, title:^(Picture-in-Picture)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="s2">&#34;pin, title:^(Picture-in-Picture)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="s2">&#34;size 480 270, title:^(Picture-in-Picture)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="s2">&#34;move 100%-490 50, title:^(Picture-in-Picture)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="c1">-- 視窗透明度（active inactive）</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="s2">&#34;opacity 0.9, class:^(kitty)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="s2">&#34;opacity 0.85 0.75, class:^(Code)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="c1">-- 防止 Electron app 自動最大化</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">        <span class="s2">&#34;suppressevent maximize, class:.*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="c1">-- XWayland video bridge（螢幕分享用）</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="s2">&#34;opacity 0.0 override, class:^(xwaylandvideobridge)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="s2">&#34;noanim, class:^(xwaylandvideobridge)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="s2">&#34;noinitialfocus, class:^(xwaylandvideobridge)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="s2">&#34;maxsize 1 1, class:^(xwaylandvideobridge)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="s2">&#34;noblur, class:^(xwaylandvideobridge)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><h3 id="查詢應用程式的-class-和-title">查詢應用程式的 class 和 title</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">hyprctl clients          <span class="c1"># 列出所有開啟的視窗（含 class、title、PID 等）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hyprctl activewindow     <span class="c1"># 當前焦點視窗的資訊</span></span></span></code></pre></div><p>Window rules 用 regex 匹配 <code>class</code> 和 <code>title</code>。靜態 rules 匹配的是視窗開啟時的初始值（<code>initialTitle</code> / <code>initialClass</code>）。</p>
<p>Window rules 是讓平鋪式桌面「不只是把所有東西硬塞格子」的關鍵——識別哪些 app 適合浮動、哪些適合指定位置，需要累積使用經驗。</p>
<blockquote>
<p><strong>[VM 可測試]</strong> Window rules 邏輯、workspace 指派、float 規則。查 class/title 也在 VM 裡能做。</p></blockquote>
<h2 id="layout-配置">Layout 配置</h2>
<h3 id="dwindle">Dwindle</h3>
<p>每個新視窗把當前區域一分為二（螺旋式切分），適合不固定視窗數量的工作流：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">dwindle</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">pseudotile</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>       <span class="c1">-- 允許 app 請求 pseudo-tiling（保留原始大小）</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="n">preserve_split</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>   <span class="c1">-- 不自動改變分割方向</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">        <span class="n">force_split</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>         <span class="c1">-- 0=跟隨滑鼠, 1=永遠左/上, 2=永遠右/下</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">        <span class="n">smart_split</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">        <span class="n">smart_resizing</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><h3 id="master">Master</h3>
<p>一個主區域 + 其餘視窗堆疊在側邊，適合「一個主力視窗 + 多個參考視窗」的模式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">master</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">new_on_top</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>       <span class="c1">-- 新視窗放 master 還是 stack</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">        <span class="n">orientation</span> <span class="o">=</span> <span class="s2">&#34;left&#34;</span><span class="p">,</span>     <span class="c1">-- master 區域位置：left / right / top / bottom / center</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">        <span class="n">mfact</span> <span class="o">=</span> <span class="mf">0.55</span><span class="p">,</span>             <span class="c1">-- master 區域佔螢幕的比例</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p>在 <code>general</code> 裡設定預設 layout：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">general</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">layout</span> <span class="o">=</span> <span class="s2">&#34;dwindle&#34;</span><span class="p">,</span>       <span class="c1">-- 或 &#34;master&#34;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><blockquote>
<p><strong>[VM 可測試]</strong> Layout 切換和參數調整的行為邏輯。</p></blockquote>
<h2 id="外觀設定">外觀設定</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">general</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="n">gaps_in</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="n">gaps_out</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="n">border_size</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="p">[</span><span class="s2">&#34;col.active_border&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;rgba(89b4faee) rgba(cba6f7ee) 45deg&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="p">[</span><span class="s2">&#34;col.inactive_border&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;rgba(313244aa)&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">decoration</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">rounding</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">blur</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">            <span class="n">enabled</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">            <span class="n">size</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span>             <span class="c1">-- 模糊半徑（越高越模糊、越吃 GPU）</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">            <span class="n">passes</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span>           <span class="c1">-- 模糊次數（越多越平滑、越重）</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">            <span class="n">vibrancy</span> <span class="o">=</span> <span class="mf">0.17</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">            <span class="n">noise</span> <span class="o">=</span> <span class="mf">0.02</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">            <span class="n">new_optimizations</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="n">shadow</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">            <span class="n">enabled</span> <span class="o">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">            <span class="n">range</span> <span class="o">=</span> <span class="mi">15</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">            <span class="n">render_power</span> <span class="o">=</span> <span class="mi">3</span><span class="p">,</span>     <span class="c1">-- 1-4，衰減曲線</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">            <span class="n">color</span> <span class="o">=</span> <span class="s2">&#34;0xee1a1a2e&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="n">dim_inactive</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="n">dim_strength</span> <span class="o">=</span> <span class="mf">0.2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="n">active_opacity</span> <span class="o">=</span> <span class="mf">1.0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">inactive_opacity</span> <span class="o">=</span> <span class="mf">0.95</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><blockquote>
<p><strong>[需實機測試]</strong> blur 的效能影響和視覺品質——這是 Hyprland 最吃 GPU 的功能。VM 裡建議 <code>blur = { enabled = false }</code> 避免卡頓。shadow 和 rounding 在軟體渲染下可以動但會很慢。</p></blockquote>
<h2 id="動畫設定">動畫設定</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- 定義 bezier 曲線</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;bezier&#34;</span><span class="p">,</span> <span class="s2">&#34;ease&#34;</span><span class="p">,</span> <span class="mf">0.25</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.25</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;bezier&#34;</span><span class="p">,</span> <span class="s2">&#34;overshot&#34;</span><span class="p">,</span> <span class="mf">0.05</span><span class="p">,</span> <span class="mf">0.9</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">,</span> <span class="mf">1.05</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">-- Spring 物理動畫（較新的替代方案）</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;spring&#34;</span><span class="p">,</span> <span class="s2">&#34;bouncy&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>   <span class="c1">-- damping, frequency, speed</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">-- 套用動畫</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;animation&#34;</span><span class="p">,</span> <span class="s2">&#34;windows&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="s2">&#34;ease&#34;</span><span class="p">,</span> <span class="s2">&#34;slide&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;animation&#34;</span><span class="p">,</span> <span class="s2">&#34;windowsOut&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="s2">&#34;ease&#34;</span><span class="p">,</span> <span class="s2">&#34;popin 80%&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;animation&#34;</span><span class="p">,</span> <span class="s2">&#34;fade&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="s2">&#34;ease&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;animation&#34;</span><span class="p">,</span> <span class="s2">&#34;workspaces&#34;</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="s2">&#34;ease&#34;</span><span class="p">,</span> <span class="s2">&#34;slide&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">-- 關閉特定動畫</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">hl.animation</span><span class="p">(</span><span class="s2">&#34;animation&#34;</span><span class="p">,</span> <span class="s2">&#34;windowsMove&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span></span></span></code></pre></div><p>VM 裡跑 Hyprland 時，建議完全關閉動畫以維持可用的操作速度：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">animations</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">        <span class="n">enabled</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><blockquote>
<p><strong>[需實機測試]</strong> 動畫流暢度是 Hyprland 的核心賣點，只有實機能評估。VM 裡的卡頓是 GPU 加速不足造成的，不代表 Hyprland 本身的效能。</p></blockquote>
<h2 id="autostart">Autostart</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">exec_once</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="c1">-- 桌面元件</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="s2">&#34;waybar&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="s2">&#34;mako&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="s2">&#34;hyprpaper&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="c1">-- 系統服務</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="s2">&#34;wl-paste --type text --watch cliphist store&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="c1">-- Idle daemon（閒置鎖屏，鎖屏安全模型見 session-lock 卡）</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="s2">&#34;hypridle&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p><code>exec_once</code> 只在 Hyprland 啟動時跑一次（config reload 不會重複執行）。如果需要每次 reload 都重跑某個指令，用 <code>exec</code>（但多數情況不需要）。</p>
<h2 id="plugin-系統">Plugin 系統</h2>
<p>Hyprland 用 <code>hyprpm</code>（Hyprland Plugin Manager）管理 plugin：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">hyprpm update                                              <span class="c1"># 更新 plugin index</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">hyprpm add https://github.com/hyprwm/hyprland-plugins     <span class="c1"># 加入官方 plugin repo</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">hyprpm <span class="nb">enable</span> hyprexpo                                     <span class="c1"># 啟用 plugin</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">hyprpm disable hyprexpo                                    <span class="c1"># 停用 plugin</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">hyprpm list                                                <span class="c1"># 列出可用 plugin</span></span></span></code></pre></div><table>
  <thead>
      <tr>
          <th>Plugin</th>
          <th>功能</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>hyprexpo</strong></td>
          <td>workspace 鳥瞰（所有 workspace 縮圖一覽）</td>
      </tr>
      <tr>
          <td><strong>hy3</strong></td>
          <td>i3/sway 風格的手動 tiling layout（替代 dwindle）</td>
      </tr>
      <tr>
          <td><strong>hyprspace</strong></td>
          <td>類似 macOS Mission Control 的 workspace 切換動畫</td>
      </tr>
      <tr>
          <td><strong>hyprbars</strong></td>
          <td>視窗標題列（可自訂按鈕）</td>
      </tr>
      <tr>
          <td><strong>hyprtrails</strong></td>
          <td>游標拖尾特效</td>
      </tr>
  </tbody>
</table>
<p>Plugin 的配置寫在 hyprland.lua 裡，是 dotfile 的一部分。</p>
<blockquote>
<p><strong>[VM 可測試]</strong> 不依賴 GPU 的 plugin（hy3、hyprbars）。<br>
<strong>[需實機測試]</strong> 視覺特效類 plugin（hyprexpo、hyprspace、hyprtrails）。</p></blockquote>
<h2 id="dotfile-結構對應">Dotfile 結構對應</h2>
<p>Hyprland 的配置拆成多個 <code>.lua</code> 檔，全部放在同一個 stow package 裡。<code>monitors.lua</code> 是硬體相關的——跨機器搬移時可能要排除或用 template/local 機制處理（見<a href="/blog/linux/dotfile/01-dotfile-management/cross-platform-one-repo/" data-link-title="跨平台共用一個 Repo" data-link-desc="macOS 跟 Linux 要共用同一個 dotfile repo、不想維護兩份時回來讀">跨平台共用一個 Repo</a>）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">~/dotfiles/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">└── hyprland/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    └── .config/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        └── hypr/
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">            ├── hyprland.lua       # 主入口（只有 require 行）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            ├── monitors.lua       # 硬體相關、可能排除或 template
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            ├── keybinds.lua
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            ├── rules.lua
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            ├── autostart.lua
</span></span><span class="line"><span class="ln">10</span><span class="cl">            ├── appearance.lua
</span></span><span class="line"><span class="ln">11</span><span class="cl">            └── env.lua</span></span></code></pre></div><h2 id="穩定性與維護的務實面">穩定性與維護的務實面</h2>
<p>Hyprland 的開發節奏快、功能更新激進。v0.55 的 Lua 遷移就是一個典型案例——配置格式整個換掉，舊的 <code>.conf</code> 只會再支援一到兩個版本。配置檔的語法和可用選項會隨版本變動。</p>
<p>應對策略：</p>
<ul>
<li>更新前先看 Hyprland wiki 的 Configuring 頁面和 changelog</li>
<li>dotfile repo 的 commit message 記錄「因應 Hyprland vX.Y 改了什麼設定」</li>
<li>如果用 Arch 的 rolling release，<code>pacman -Syu</code> 前先確認 Hyprland 是否有 breaking change（Arch 社群通常會在論壇預警）</li>
<li>官方提供遷移工具（如 <code>hyprlang2lua</code>），格式變更時優先使用</li>
</ul>
<p>這是<a href="/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">視窗管理與平鋪式工作流</a>提過的代價——把日常桌面建立在高速移動的專案上，持續的配置維護是實際成本。</p>
<h2 id="vm-與實機測試對照">VM 與實機測試對照</h2>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>VM 可測試</th>
          <th>需實機測試</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>配置語法 / 結構</td>
          <td>可</td>
          <td></td>
      </tr>
      <tr>
          <td>Keybind 設計</td>
          <td>可</td>
          <td></td>
      </tr>
      <tr>
          <td>Window rules 邏輯</td>
          <td>可</td>
          <td></td>
      </tr>
      <tr>
          <td>Workspace 切換</td>
          <td>可</td>
          <td></td>
      </tr>
      <tr>
          <td>Layout 參數</td>
          <td>可</td>
          <td></td>
      </tr>
      <tr>
          <td>Autostart 順序</td>
          <td>可</td>
          <td></td>
      </tr>
      <tr>
          <td>Plugin 配置</td>
          <td>部分</td>
          <td>視覺類</td>
      </tr>
      <tr>
          <td>動畫流暢度</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>Blur 效能 / 品質</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>多螢幕排列</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>HiDPI 縮放</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>觸控板 / 手勢</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>媒體鍵 / 亮度鍵</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>NVIDIA 驅動設定</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>螢幕分享</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>休眠 / 喚醒</td>
          <td></td>
          <td>必要</td>
      </tr>
      <tr>
          <td>合蓋行為</td>
          <td></td>
          <td>必要</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item><item><title>Caelestia 安裝</title><link>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-installation/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-installation/</guid><description>&lt;p>Caelestia 的安裝有兩條路：用 CLI 工具一鍵部署完整 dotfiles，或只安裝 shell 元件保留自己的 Hyprland 配置。兩者的前提都是 Hyprland 已經安裝且能正常啟動。&lt;/p>
&lt;h2 id="前提條件">前提條件&lt;/h2>
&lt;ul>
&lt;li>Arch Linux（或 Arch 系發行版如 CachyOS、EndeavourOS）&lt;/li>
&lt;li>Hyprland 已安裝且能從 TTY 啟動（見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-installation/" data-link-title="Hyprland 安裝與環境建置" data-link-desc="要在 Arch Linux 上從零安裝 Hyprland 桌面環境時回來讀">Hyprland 安裝指南&lt;/a>）&lt;/li>
&lt;li>AUR helper 已安裝（yay 或 paru）&lt;/li>
&lt;li>網路連線（安裝過程需要拉 AUR 套件和 Git repo）&lt;/li>
&lt;/ul>
&lt;h2 id="推薦方式cli-完整安裝">推薦方式：CLI 完整安裝&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">paru -S caelestia-cli
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">caelestia install&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>caelestia install&lt;/code> 做的事：&lt;/p>
&lt;ol>
&lt;li>從 GitHub clone Caelestia dotfiles repo&lt;/li>
&lt;li>安裝所有 runtime 依賴（透過 AUR helper）&lt;/li>
&lt;li>部署配置檔到 &lt;code>~/.config/&lt;/code> 對應位置&lt;/li>
&lt;li>設定 Hyprland 載入 Caelestia shell&lt;/li>
&lt;/ol>
&lt;p>安裝完成後重新啟動 Hyprland，Caelestia 會自動載入。&lt;/p>
&lt;p>&lt;strong>注意&lt;/strong>：&lt;code>caelestia install&lt;/code> 會覆寫你現有的 Hyprland 配置。如果你已經有自己的 hyprland.conf / hyprland.lua，先備份。安裝後可以透過 &lt;code>~/.config/caelestia/hypr-user.lua&lt;/code> 加入自訂設定。&lt;/p>
&lt;h2 id="shell-only-安裝">Shell-only 安裝&lt;/h2>
&lt;p>只裝 UI 元件，不動 Hyprland config 和其他應用程式設定：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">yay -S caelestia-shell&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>啟動方式：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">caelestia shell -d &lt;span class="c1"># daemonized，背景執行&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># 或&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">qs -c caelestia &lt;span class="c1"># 透過 quickshell 直接啟動&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Shell 不會因為裝了就自動啟動——Hyprland 開機只執行 config 裡 &lt;code>exec-once&lt;/code> 列出的程式，漏列的結果是登入後桌面沒有 bar 也沒有通知（實測：手動啟動用了幾天、直到主機重開才暴露從沒進 autostart）。把啟動指令加進 &lt;code>hyprland.conf&lt;/code>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="na">exec-once&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">caelestia shell -d&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Caelestia 自帶 bar、通知、鎖屏與 launcher，跟獨立的 waybar / mako 互斥：通知的 D-Bus name（&lt;code>org.freedesktop.Notifications&lt;/code>）同一時間只有一個擁有者、bar 會疊兩條。設 Caelestia 為主 shell 時，把 waybar / mako 從 &lt;code>exec-once&lt;/code> 移除（註解掉可留作停用 Caelestia 時的 fallback）。&lt;/p>
&lt;h2 id="aur-套件一覽">AUR 套件一覽&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>套件&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>caelestia-shell&lt;/code>&lt;/td>
 &lt;td>穩定版 shell（UI 元件）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>caelestia-shell-git&lt;/code>&lt;/td>
 &lt;td>開發版 shell（最新功能，可能不穩定）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>caelestia-cli&lt;/code>&lt;/td>
 &lt;td>CLI 工具（安裝、主題切換、截圖、錄影等）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;code>caelestia-shell&lt;/code> 是 &lt;code>caelestia-cli&lt;/code> 的 &lt;strong>optional dependency&lt;/strong>（提供 shell 控制與截圖功能）——只裝 CLI 不會自動拉 shell，兩個都要就各自明講：&lt;code>yay -S caelestia-cli caelestia-shell&lt;/code>（實測 caelestia-cli 1.1.1 的 PKGBUILD，shell 列在 optdepends）。&lt;/p>
&lt;h2 id="手動-build">手動 Build&lt;/h2>
&lt;p>從原始碼 build shell（不使用 AUR）：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$XDG_CONFIG_HOME&lt;/span>&lt;span class="s2">/quickshell&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">git clone https://github.com/caelestia-dots/shell.git caelestia
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="nb">cd&lt;/span> caelestia
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">cmake -B build -G Ninja -DCMAKE_BUILD_TYPE&lt;span class="o">=&lt;/span>Release -DCMAKE_INSTALL_PREFIX&lt;span class="o">=&lt;/span>/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">cmake --build build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">sudo cmake --install build&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="runtime-依賴">Runtime 依賴&lt;/h2>
&lt;h3 id="shell-依賴">Shell 依賴&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>套件&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>quickshell-git&lt;/td>
 &lt;td>Quickshell 框架（穩定版不夠用）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ddcutil&lt;/td>
 &lt;td>外接螢幕亮度控制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>brightnessctl&lt;/td>
 &lt;td>筆電螢幕亮度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>libcava&lt;/td>
 &lt;td>音訊視覺化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>networkmanager&lt;/td>
 &lt;td>網路管理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>lm-sensors&lt;/td>
 &lt;td>硬體溫度感測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>fish&lt;/td>
 &lt;td>Fish shell（部分功能依賴）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>aubio&lt;/td>
 &lt;td>音訊分析&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>libpipewire&lt;/td>
 &lt;td>PipeWire 音訊整合&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>qt6-declarative&lt;/td>
 &lt;td>QML runtime&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>material-symbols (font)&lt;/td>
 &lt;td>Material Design icon 字型&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>caskaydia-cove-nerd&lt;/td>
 &lt;td>Nerd Font&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>quickshell-git 是硬性需求&lt;/strong>。穩定版的 quickshell 缺少 Caelestia 需要的 API，安裝穩定版會導致 shell 無法啟動。&lt;/p></description><content:encoded><![CDATA[<p>Caelestia 的安裝有兩條路：用 CLI 工具一鍵部署完整 dotfiles，或只安裝 shell 元件保留自己的 Hyprland 配置。兩者的前提都是 Hyprland 已經安裝且能正常啟動。</p>
<h2 id="前提條件">前提條件</h2>
<ul>
<li>Arch Linux（或 Arch 系發行版如 CachyOS、EndeavourOS）</li>
<li>Hyprland 已安裝且能從 TTY 啟動（見 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-installation/" data-link-title="Hyprland 安裝與環境建置" data-link-desc="要在 Arch Linux 上從零安裝 Hyprland 桌面環境時回來讀">Hyprland 安裝指南</a>）</li>
<li>AUR helper 已安裝（yay 或 paru）</li>
<li>網路連線（安裝過程需要拉 AUR 套件和 Git repo）</li>
</ul>
<h2 id="推薦方式cli-完整安裝">推薦方式：CLI 完整安裝</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">paru -S caelestia-cli
</span></span><span class="line"><span class="ln">2</span><span class="cl">caelestia install</span></span></code></pre></div><p><code>caelestia install</code> 做的事：</p>
<ol>
<li>從 GitHub clone Caelestia dotfiles repo</li>
<li>安裝所有 runtime 依賴（透過 AUR helper）</li>
<li>部署配置檔到 <code>~/.config/</code> 對應位置</li>
<li>設定 Hyprland 載入 Caelestia shell</li>
</ol>
<p>安裝完成後重新啟動 Hyprland，Caelestia 會自動載入。</p>
<p><strong>注意</strong>：<code>caelestia install</code> 會覆寫你現有的 Hyprland 配置。如果你已經有自己的 hyprland.conf / hyprland.lua，先備份。安裝後可以透過 <code>~/.config/caelestia/hypr-user.lua</code> 加入自訂設定。</p>
<h2 id="shell-only-安裝">Shell-only 安裝</h2>
<p>只裝 UI 元件，不動 Hyprland config 和其他應用程式設定：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">yay -S caelestia-shell</span></span></code></pre></div><p>啟動方式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">caelestia shell -d    <span class="c1"># daemonized，背景執行</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 或</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">qs -c caelestia       <span class="c1"># 透過 quickshell 直接啟動</span></span></span></code></pre></div><p>Shell 不會因為裝了就自動啟動——Hyprland 開機只執行 config 裡 <code>exec-once</code> 列出的程式，漏列的結果是登入後桌面沒有 bar 也沒有通知（實測：手動啟動用了幾天、直到主機重開才暴露從沒進 autostart）。把啟動指令加進 <code>hyprland.conf</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ini" data-lang="ini"><span class="line"><span class="ln">1</span><span class="cl"><span class="na">exec-once</span> <span class="o">=</span> <span class="s">caelestia shell -d</span></span></span></code></pre></div><p>Caelestia 自帶 bar、通知、鎖屏與 launcher，跟獨立的 waybar / mako 互斥：通知的 D-Bus name（<code>org.freedesktop.Notifications</code>）同一時間只有一個擁有者、bar 會疊兩條。設 Caelestia 為主 shell 時，把 waybar / mako 從 <code>exec-once</code> 移除（註解掉可留作停用 Caelestia 時的 fallback）。</p>
<h2 id="aur-套件一覽">AUR 套件一覽</h2>
<table>
  <thead>
      <tr>
          <th>套件</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>caelestia-shell</code></td>
          <td>穩定版 shell（UI 元件）</td>
      </tr>
      <tr>
          <td><code>caelestia-shell-git</code></td>
          <td>開發版 shell（最新功能，可能不穩定）</td>
      </tr>
      <tr>
          <td><code>caelestia-cli</code></td>
          <td>CLI 工具（安裝、主題切換、截圖、錄影等）</td>
      </tr>
  </tbody>
</table>
<p><code>caelestia-shell</code> 是 <code>caelestia-cli</code> 的 <strong>optional dependency</strong>（提供 shell 控制與截圖功能）——只裝 CLI 不會自動拉 shell，兩個都要就各自明講：<code>yay -S caelestia-cli caelestia-shell</code>（實測 caelestia-cli 1.1.1 的 PKGBUILD，shell 列在 optdepends）。</p>
<h2 id="手動-build">手動 Build</h2>
<p>從原始碼 build shell（不使用 AUR）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">cd</span> <span class="s2">&#34;</span><span class="nv">$XDG_CONFIG_HOME</span><span class="s2">/quickshell&#34;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">git clone https://github.com/caelestia-dots/shell.git caelestia
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">cd</span> caelestia
</span></span><span class="line"><span class="ln">4</span><span class="cl">cmake -B build -G Ninja -DCMAKE_BUILD_TYPE<span class="o">=</span>Release -DCMAKE_INSTALL_PREFIX<span class="o">=</span>/
</span></span><span class="line"><span class="ln">5</span><span class="cl">cmake --build build
</span></span><span class="line"><span class="ln">6</span><span class="cl">sudo cmake --install build</span></span></code></pre></div><h2 id="runtime-依賴">Runtime 依賴</h2>
<h3 id="shell-依賴">Shell 依賴</h3>
<table>
  <thead>
      <tr>
          <th>套件</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>quickshell-git</td>
          <td>Quickshell 框架（穩定版不夠用）</td>
      </tr>
      <tr>
          <td>ddcutil</td>
          <td>外接螢幕亮度控制</td>
      </tr>
      <tr>
          <td>brightnessctl</td>
          <td>筆電螢幕亮度</td>
      </tr>
      <tr>
          <td>libcava</td>
          <td>音訊視覺化</td>
      </tr>
      <tr>
          <td>networkmanager</td>
          <td>網路管理</td>
      </tr>
      <tr>
          <td>lm-sensors</td>
          <td>硬體溫度感測</td>
      </tr>
      <tr>
          <td>fish</td>
          <td>Fish shell（部分功能依賴）</td>
      </tr>
      <tr>
          <td>aubio</td>
          <td>音訊分析</td>
      </tr>
      <tr>
          <td>libpipewire</td>
          <td>PipeWire 音訊整合</td>
      </tr>
      <tr>
          <td>qt6-declarative</td>
          <td>QML runtime</td>
      </tr>
      <tr>
          <td>material-symbols (font)</td>
          <td>Material Design icon 字型</td>
      </tr>
      <tr>
          <td>caskaydia-cove-nerd</td>
          <td>Nerd Font</td>
      </tr>
  </tbody>
</table>
<p><strong>quickshell-git 是硬性需求</strong>。穩定版的 quickshell 缺少 Caelestia 需要的 API，安裝穩定版會導致 shell 無法啟動。</p>
<h3 id="cli-依賴">CLI 依賴</h3>
<table>
  <thead>
      <tr>
          <th>套件</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>libnotify</td>
          <td>通知發送</td>
      </tr>
      <tr>
          <td>swappy</td>
          <td>截圖標註</td>
      </tr>
      <tr>
          <td>grim</td>
          <td>Wayland 截圖</td>
      </tr>
      <tr>
          <td>dart-sass</td>
          <td>SCSS 編譯</td>
      </tr>
      <tr>
          <td>wl-clipboard</td>
          <td>剪貼簿</td>
      </tr>
      <tr>
          <td>slurp</td>
          <td>區域選取</td>
      </tr>
      <tr>
          <td>gpu-screen-recorder</td>
          <td>螢幕錄影</td>
      </tr>
      <tr>
          <td>glib2</td>
          <td>GLib 工具</td>
      </tr>
      <tr>
          <td>cliphist</td>
          <td>剪貼簿歷史</td>
      </tr>
      <tr>
          <td>fuzzel</td>
          <td>模糊搜尋選單</td>
      </tr>
  </tbody>
</table>
<h2 id="登入管理器">登入管理器</h2>
<p>Caelestia 不含登入管理器。推薦用 greetd + tuigreet：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S greetd greetd-tuigreet</span></span></code></pre></div><p><code>/etc/greetd/config.toml</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">terminal</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">vt</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">[</span><span class="nx">default_session</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="nx">command</span> <span class="p">=</span> <span class="s2">&#34;tuigreet --cmd Hyprland&#34;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="nx">user</span> <span class="p">=</span> <span class="s2">&#34;greeter&#34;</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo systemctl <span class="nb">enable</span> greetd</span></span></code></pre></div><p>也可以不裝登入管理器，直接從 TTY 啟動：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 登入 TTY 後</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">Hyprland</span></span></code></pre></div><h2 id="full-dotfiles-管理範圍">Full Dotfiles 管理範圍</h2>
<p><code>caelestia install</code> 部署的完整 dotfiles 不只是 shell，還包括：</p>
<ul>
<li>Hyprland config（Lua 格式）</li>
<li>Firefox / Zen Browser 設定</li>
<li>VSCode / Zed 設定</li>
<li>Fish shell config</li>
<li>Foot terminal config</li>
<li>Starship prompt</li>
<li>Btop</li>
<li>Fastfetch</li>
<li>Thunar 檔案管理器</li>
</ul>
<p>這是 Caelestia 「一套 rice」的完整範圍。如果你只想用 shell 元件、保留自己的應用程式配置，用 shell-only 安裝。</p>
<h2 id="cli-常用指令">CLI 常用指令</h2>
<table>
  <thead>
      <tr>
          <th>指令</th>
          <th>功能</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>caelestia shell -d</code></td>
          <td>啟動 shell（背景）</td>
      </tr>
      <tr>
          <td><code>caelestia shell -s</code></td>
          <td>列出所有 IPC 指令</td>
      </tr>
      <tr>
          <td><code>caelestia install</code></td>
          <td>完整安裝 dotfiles</td>
      </tr>
      <tr>
          <td><code>caelestia update</code></td>
          <td>系統 + dotfiles 更新</td>
      </tr>
      <tr>
          <td><code>caelestia scheme set -n dynamic</code></td>
          <td>設定動態配色方案</td>
      </tr>
      <tr>
          <td><code>caelestia wallpaper -f &lt;path&gt;</code></td>
          <td>設定桌布</td>
      </tr>
      <tr>
          <td><code>caelestia screenshot</code></td>
          <td>截圖</td>
      </tr>
      <tr>
          <td><code>caelestia record</code></td>
          <td>螢幕錄影</td>
      </tr>
      <tr>
          <td><code>caelestia clipboard</code></td>
          <td>剪貼簿歷史</td>
      </tr>
      <tr>
          <td><code>caelestia emoji</code></td>
          <td>Emoji / glyph 選取器</td>
      </tr>
      <tr>
          <td><code>caelestia toggle</code></td>
          <td>切換特殊工作區</td>
      </tr>
      <tr>
          <td><code>caelestia resizer</code></td>
          <td>視窗 resize daemon</td>
      </tr>
  </tbody>
</table>
<h2 id="首次啟動常見問題">首次啟動常見問題</h2>
<p><strong>黑屏</strong>：通常是缺少 <code>xdg-desktop-portal-hyprland</code>。確認已安裝：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pacman -Q xdg-desktop-portal-hyprland</span></span></code></pre></div><p><strong>Shell 沒有載入</strong>：確認 quickshell-git（不是 quickshell 穩定版）已安裝，且 Hyprland 的 exec-once 有啟動 Caelestia。</p>
<p><strong>字型 icon 顯示為方塊</strong>：缺少 Material Symbols 和 Nerd Font。安裝：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">yay -S material-symbols ttf-caskaydia-cove-nerd</span></span></code></pre></div><h2 id="vm-測試-vs-實機測試">VM 測試 vs 實機測試</h2>
<p><strong>VM 可測試</strong>：安裝流程完整性、CLI 指令是否正常、配置檔結構和語法、啟動器功能、通知系統行為、配置 reload。</p>
<p><strong>需實機測試</strong>：動畫流暢度和幀率、blur 品質和效能影響、Material Design 3 動態取色品質、多螢幕佈局、daily-use 的回應速度和穩定性。</p>
<p>VM 中 Caelestia 的 blur、動畫、動態取色會極度降級或無法運作（軟體渲染沒有足夠的 GPU 加速）。VM 適合驗證「裝得起來、config 能讀」，不適合評估視覺效果和日常使用體驗。</p>
]]></content:encoded></item><item><title>Hyprland VM 環境設定與測試矩陣</title><link>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/</guid><description>&lt;p>VM 是 Hyprland 配置的演練場——用來驗證「配置邏輯對不對」，不是用來體驗「跑起來順不順」。GPU 加速在 VM 中受限，動畫和模糊效果會嚴重降級或無法使用，但配置檔語法、keybind 設計、window rules、workspace 邏輯都能在 VM 中完整測試。&lt;/p>
&lt;h2 id="utm-on-apple-silicon-設定">UTM on Apple Silicon 設定&lt;/h2>
&lt;p>Apple Silicon Mac 用 UTM（基於 QEMU）跑 ARM64 Linux VM：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>CPU&lt;/strong>：UTM 使用 Apple Hypervisor.framework，ARM64 guest 接近原生速度&lt;/li>
&lt;li>&lt;strong>GPU&lt;/strong>：使用 &lt;code>virtio-gpu-gl-pci&lt;/code>（UTM v4.1+ 新建 Linux VM 的預設）。UTM v5.0.0+ 的 GitHub release 版支援 Venus driver（guest Mesa → host MoltenVK → Apple Metal 的 Vulkan 轉送路徑），這是目前最好的 GPU 加速方案&lt;/li>
&lt;li>&lt;strong>Linux ISO&lt;/strong>：Arch Linux ARM（archlinuxarm.org）或 Fedora aarch64&lt;/li>
&lt;li>&lt;strong>建議配置&lt;/strong>：4 CPU cores、4GB+ RAM、40GB+ disk&lt;/li>
&lt;/ul>
&lt;h3 id="utm-建-vm-的注意事項">UTM 建 VM 的注意事項&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>第一頁選 Virtualize，不是 Emulate&lt;/strong>——同架構（Apple Silicon 跑 ARM64 guest）兩條都是 QEMU，差在 Virtualize 走 hvf 硬體虛擬化（CPU 接近原生）、Emulate 走 TCG 純軟體模擬（CPU 慢一個數量級；實測 C++ 大型編譯一小時只跑 1/3）。Emulate 只在跨架構（ARM Mac 跑 x86_64 guest）才需要。判別現有 VM：guest 裡 &lt;code>lscpu&lt;/code> 的 Model name 是 &lt;code>-&lt;/code> 為直通、顯示具體型號（如 Cortex-A72）為模擬&lt;/li>
&lt;li>選 「Linux」 虛擬機類型，不是 「Other」&lt;/li>
&lt;li>Display Card 選 &lt;code>virtio-gpu-gl-pci&lt;/code>（有 3D 加速），不是 &lt;code>virtio-gpu-pci&lt;/code>（無加速）；Emulate 精靈預設給無加速的 &lt;code>virtio-gpu-pci&lt;/code>、Virtualize 精靈通常直接給對&lt;/li>
&lt;li>&lt;strong>VM 視窗可能停在序列視圖、圖形顯示是另一個視圖&lt;/strong>——Virtualize 精靈預設多附一個序列裝置，UTM 視窗開機後可能顯示的是序列 console（文字登入、guest 裡 &lt;code>who&lt;/code> 顯示登入在 &lt;code>pts/0&lt;/code>），跟 virtio-gpu 的圖形輸出是兩個獨立視圖。切換：VM 視窗工具列的顯示器下拉選單 → 選 &lt;code>Display 1 (virtio-gpu)&lt;/code>。判讀圖形裝置本身有沒有掛上看 guest 的 &lt;code>ls /dev/dri/&lt;/code>（有 &lt;code>card0&lt;/code> = 裝置在、只是視窗看錯視圖）。不想每次切就到 VM 設定移除序列裝置。compositor 要在圖形視圖那側的 VT 上啟動、序列 console 起不了 Hyprland&lt;/li>
&lt;li>如果用 App Store 版的 UTM（不含 Venus），只有基本的 virtio-gpu-gl 加速&lt;/li>
&lt;li>GitHub release 版的 UTM 支援 Venus/MoltenVK，效能更好但仍不及裸金屬&lt;/li>
&lt;li>&lt;strong>修飾鍵：Mac 的 ⌘ 對應 guest 的 &lt;code>SUPER&lt;/code>，但 macOS 會先攔截部分 ⌘ 組合&lt;/strong>——Hyprland 的 keybind 幾乎都以 &lt;code>SUPER&lt;/code>（Meta）當主修飾鍵（見 &lt;a href="../hyprland-core-config/">核心配置的修飾鍵段&lt;/a>），而 UTM 裡 Mac 的 Command ⌘ 通常就是那顆 &lt;code>SUPER&lt;/code>。問題是 macOS 自己會先吃掉某些 ⌘ 組合（⌘+Q 結束 app、⌘+Space Spotlight…），VM 收不到。判讀：&lt;code>SUPER&lt;/code> 綁定沒反應、但焦點視窗打字正常，多半是宿主層攔截、不是 Hyprland 配置錯。解法：先確認 VM 視窗有 focus；在 UTM 的鍵盤/輸入設定開「把系統快捷鍵送進 VM（capture input）」讓 ⌘ 組合進 guest。臨時繞過：需要重載配置這類動作，直接在已開的終端機下指令（如 &lt;code>source&lt;/code> / &lt;code>hyprctl reload&lt;/code>），不必卡在 ⌘ 鍵上&lt;/li>
&lt;/ul>
&lt;h2 id="vm-必要環境變數">VM 必要環境變數&lt;/h2>
&lt;p>在 Hyprland 配置裡加入以下環境變數（只在 VM 中使用，實機要移除）：&lt;/p></description><content:encoded><![CDATA[<p>VM 是 Hyprland 配置的演練場——用來驗證「配置邏輯對不對」，不是用來體驗「跑起來順不順」。GPU 加速在 VM 中受限，動畫和模糊效果會嚴重降級或無法使用，但配置檔語法、keybind 設計、window rules、workspace 邏輯都能在 VM 中完整測試。</p>
<h2 id="utm-on-apple-silicon-設定">UTM on Apple Silicon 設定</h2>
<p>Apple Silicon Mac 用 UTM（基於 QEMU）跑 ARM64 Linux VM：</p>
<ul>
<li><strong>CPU</strong>：UTM 使用 Apple Hypervisor.framework，ARM64 guest 接近原生速度</li>
<li><strong>GPU</strong>：使用 <code>virtio-gpu-gl-pci</code>（UTM v4.1+ 新建 Linux VM 的預設）。UTM v5.0.0+ 的 GitHub release 版支援 Venus driver（guest Mesa → host MoltenVK → Apple Metal 的 Vulkan 轉送路徑），這是目前最好的 GPU 加速方案</li>
<li><strong>Linux ISO</strong>：Arch Linux ARM（archlinuxarm.org）或 Fedora aarch64</li>
<li><strong>建議配置</strong>：4 CPU cores、4GB+ RAM、40GB+ disk</li>
</ul>
<h3 id="utm-建-vm-的注意事項">UTM 建 VM 的注意事項</h3>
<ul>
<li><strong>第一頁選 Virtualize，不是 Emulate</strong>——同架構（Apple Silicon 跑 ARM64 guest）兩條都是 QEMU，差在 Virtualize 走 hvf 硬體虛擬化（CPU 接近原生）、Emulate 走 TCG 純軟體模擬（CPU 慢一個數量級；實測 C++ 大型編譯一小時只跑 1/3）。Emulate 只在跨架構（ARM Mac 跑 x86_64 guest）才需要。判別現有 VM：guest 裡 <code>lscpu</code> 的 Model name 是 <code>-</code> 為直通、顯示具體型號（如 Cortex-A72）為模擬</li>
<li>選 「Linux」 虛擬機類型，不是 「Other」</li>
<li>Display Card 選 <code>virtio-gpu-gl-pci</code>（有 3D 加速），不是 <code>virtio-gpu-pci</code>（無加速）；Emulate 精靈預設給無加速的 <code>virtio-gpu-pci</code>、Virtualize 精靈通常直接給對</li>
<li><strong>VM 視窗可能停在序列視圖、圖形顯示是另一個視圖</strong>——Virtualize 精靈預設多附一個序列裝置，UTM 視窗開機後可能顯示的是序列 console（文字登入、guest 裡 <code>who</code> 顯示登入在 <code>pts/0</code>），跟 virtio-gpu 的圖形輸出是兩個獨立視圖。切換：VM 視窗工具列的顯示器下拉選單 → 選 <code>Display 1 (virtio-gpu)</code>。判讀圖形裝置本身有沒有掛上看 guest 的 <code>ls /dev/dri/</code>（有 <code>card0</code> = 裝置在、只是視窗看錯視圖）。不想每次切就到 VM 設定移除序列裝置。compositor 要在圖形視圖那側的 VT 上啟動、序列 console 起不了 Hyprland</li>
<li>如果用 App Store 版的 UTM（不含 Venus），只有基本的 virtio-gpu-gl 加速</li>
<li>GitHub release 版的 UTM 支援 Venus/MoltenVK，效能更好但仍不及裸金屬</li>
<li><strong>修飾鍵：Mac 的 ⌘ 對應 guest 的 <code>SUPER</code>，但 macOS 會先攔截部分 ⌘ 組合</strong>——Hyprland 的 keybind 幾乎都以 <code>SUPER</code>（Meta）當主修飾鍵（見 <a href="../hyprland-core-config/">核心配置的修飾鍵段</a>），而 UTM 裡 Mac 的 Command ⌘ 通常就是那顆 <code>SUPER</code>。問題是 macOS 自己會先吃掉某些 ⌘ 組合（⌘+Q 結束 app、⌘+Space Spotlight…），VM 收不到。判讀：<code>SUPER</code> 綁定沒反應、但焦點視窗打字正常，多半是宿主層攔截、不是 Hyprland 配置錯。解法：先確認 VM 視窗有 focus；在 UTM 的鍵盤/輸入設定開「把系統快捷鍵送進 VM（capture input）」讓 ⌘ 組合進 guest。臨時繞過：需要重載配置這類動作，直接在已開的終端機下指令（如 <code>source</code> / <code>hyprctl reload</code>），不必卡在 ⌘ 鍵上</li>
</ul>
<h2 id="vm-必要環境變數">VM 必要環境變數</h2>
<p>在 Hyprland 配置裡加入以下環境變數（只在 VM 中使用，實機要移除）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">-- VM-only environment variables</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s2">&#34;WLR_RENDERER_ALLOW_SOFTWARE, 1&#34;</span><span class="p">,</span>  <span class="c1">-- 允許軟體渲染 fallback</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s2">&#34;WLR_NO_HARDWARE_CURSORS, 1&#34;</span><span class="p">,</span>      <span class="c1">-- 停用硬體 cursor（VM 常見問題）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="s2">&#34;LIBGL_ALWAYS_SOFTWARE, 1&#34;</span><span class="p">,</span>        <span class="c1">-- 強制 Mesa 軟體渲染</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>如果上述仍無法啟動（virtio-gpu vGPU passthrough 的情況）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">env</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="s2">&#34;AQ_NO_KMS_REQUIREMENT, 1&#34;</span><span class="p">,</span>       <span class="c1">-- 繞過 KMS 需求</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="s2">&#34;WLR_RENDERER, pixman&#34;</span><span class="p">,</span>            <span class="c1">-- 強制 pixman 軟體 renderer</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><blockquote>
<p><strong>[已驗證]</strong> Hyprland 0.55（Aquamarine）+ UTM virtio-gpu-gl-pci 實測：GPU 加速模式下 Hyprland 直接走 VirGL/Venus，不需要 <code>WLR_RENDERER</code> 或 <code>WLR_RENDERER_ALLOW_SOFTWARE</code>。<code>AQ_NO_KMS_REQUIREMENT</code> 仍有效。軟體渲染 fallback 路徑（<code>WLR_RENDERER=pixman</code>）未測試——有 GPU 加速時不需要走這條。</p></blockquote>
<h2 id="vm-中應該關閉的效果">VM 中應該關閉的效果</h2>
<p>軟體渲染下，視覺效果是最大的效能殺手。建議在 VM 配置中停用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="n">decoration</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="n">blur</span> <span class="o">=</span> <span class="p">{</span> <span class="n">enabled</span> <span class="o">=</span> <span class="kc">false</span> <span class="p">},</span>     <span class="c1">-- 模糊是 GPU 最重的效果</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="n">shadow</span> <span class="o">=</span> <span class="p">{</span> <span class="n">enabled</span> <span class="o">=</span> <span class="kc">false</span> <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="n">rounding</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">animations</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="n">enabled</span> <span class="o">=</span> <span class="kc">false</span><span class="p">,</span>                <span class="c1">-- 或設定極簡動畫</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p>這些效果關閉後，基本的平鋪操作（切換視窗、移動 workspace、開關 app）在 VM 中應該足夠流暢。</p>
<h2 id="效能預期">效能預期</h2>
<table>
  <thead>
      <tr>
          <th>功能</th>
          <th>軟體渲染</th>
          <th>virtio-gpu-gl</th>
          <th>裸金屬（實機）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>基本平鋪操作</td>
          <td>可用</td>
          <td>順暢</td>
          <td>順暢</td>
      </tr>
      <tr>
          <td>視窗動畫</td>
          <td>卡頓明顯</td>
          <td>勉強可接受</td>
          <td>流暢</td>
      </tr>
      <tr>
          <td>模糊/透明</td>
          <td>無法使用</td>
          <td>卡頓</td>
          <td>流暢</td>
      </tr>
      <tr>
          <td>Waybar + Wofi</td>
          <td>正常</td>
          <td>正常</td>
          <td>正常</td>
      </tr>
      <tr>
          <td>多 Workspace 切換</td>
          <td>正常</td>
          <td>正常</td>
          <td>正常</td>
      </tr>
      <tr>
          <td>Firefox 瀏覽</td>
          <td>明顯變慢</td>
          <td>可用</td>
          <td>正常</td>
      </tr>
  </tbody>
</table>
<p>VM 的價值在於驗證配置邏輯，不在於評估視覺體驗。如果在 VM 裡覺得「卡」，不代表 Hyprland 本身慢——多數情況是 VM 圖形加速的限制。</p>
<h2 id="sway-作為-vm-初步驗證工具">Sway 作為 VM 初步驗證工具</h2>
<p>如果 VM 中 Hyprland 跑得太吃力，可以先用 Sway 驗證 VM 的 Wayland 圖形棧是否正常：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sudo pacman -S sway foot
</span></span><span class="line"><span class="ln">2</span><span class="cl">sway</span></span></code></pre></div><p>Sway 比 Hyprland 輕量（基於 wlroots、沒有華麗動畫），如果 Sway 能跑，代表 VM 的 Wayland 環境是正常的，Hyprland 的問題只是效能不夠。如果連 Sway 都跑不動，要回去檢查 VM 的 GPU 設定。</p>
<h2 id="vm-vs-實機測試矩陣">VM vs 實機測試矩陣</h2>
<h3 id="vm-中可完整驗證">VM 中可完整驗證</h3>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>配置檔語法</td>
          <td>Lua config 是否解析正確、require 拆分是否正常</td>
      </tr>
      <tr>
          <td>Keybind 設計</td>
          <td>快捷鍵邏輯、submap、modifier 組合</td>
      </tr>
      <tr>
          <td>Window rules</td>
          <td>float / workspace assignment / opacity 規則是否生效</td>
      </tr>
      <tr>
          <td>Workspace 切換</td>
          <td>workspace 編號、切換邏輯</td>
      </tr>
      <tr>
          <td>Layout 選擇</td>
          <td>dwindle vs master 的行為差異</td>
      </tr>
      <tr>
          <td>Waybar 模組配置</td>
          <td>JSON config + CSS styling 是否正確顯示</td>
      </tr>
      <tr>
          <td>Wofi/Rofi 主題</td>
          <td>啟動器的功能和外觀設定</td>
      </tr>
      <tr>
          <td>Mako 通知樣式</td>
          <td>通知的位置、配色、timeout</td>
      </tr>
      <tr>
          <td>Hyprlock 佈局</td>
          <td>鎖屏的輸入框位置和文字配置</td>
      </tr>
      <tr>
          <td>Autostart 順序</td>
          <td>exec-once 的程式是否正確啟動</td>
      </tr>
      <tr>
          <td>環境變數</td>
          <td>XDG、Qt、GTK 等環境變數是否正確設定</td>
      </tr>
      <tr>
          <td>Stow 部署</td>
          <td>dotfile repo 的 stow 是否正確建立 symlink</td>
      </tr>
      <tr>
          <td>Bootstrap script</td>
          <td>install.sh 的完整流程（安裝套件 + deploy 配置）</td>
      </tr>
      <tr>
          <td>Caelestia CLI 指令</td>
          <td><code>caelestia shell</code>、<code>caelestia scheme</code> 等指令是否可執行</td>
      </tr>
  </tbody>
</table>
<h3 id="需要實機測試">需要實機測試</h3>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>為什麼 VM 不行</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>多螢幕配置</td>
          <td>VM 通常只有一個虛擬顯示器</td>
      </tr>
      <tr>
          <td>HiDPI / fractional scaling</td>
          <td>虛擬顯示器不模擬真實解析度行為</td>
      </tr>
      <tr>
          <td>VRR / Adaptive Sync</td>
          <td>需要支援 VRR 的真實螢幕</td>
      </tr>
      <tr>
          <td>動畫流暢度</td>
          <td>VM 的 GPU 加速不足以評估真實效能</td>
      </tr>
      <tr>
          <td>模糊效果品質</td>
          <td>軟體渲染下模糊不可用或品質差</td>
      </tr>
      <tr>
          <td>觸控板 / 手勢</td>
          <td>VM 沒有觸控板裝置</td>
      </tr>
      <tr>
          <td>媒體鍵 / 亮度鍵</td>
          <td>需要實體鍵盤上的 XF86 keycodes</td>
      </tr>
      <tr>
          <td>NVIDIA 驅動設定</td>
          <td>VM 不走 NVIDIA 驅動，所有 NVIDIA 配置無法測試</td>
      </tr>
      <tr>
          <td>Screen sharing</td>
          <td>PipeWire + portal 的完整鏈路在 VM 中測試無意義</td>
      </tr>
      <tr>
          <td>Suspend / Resume</td>
          <td>虛擬機的 suspend 行為跟實機不同</td>
      </tr>
      <tr>
          <td>硬體 cursor 渲染</td>
          <td>VM 用軟體 cursor，無法測試硬體 cursor 問題</td>
      </tr>
      <tr>
          <td>藍牙 / WiFi 整合</td>
          <td>需要實際硬體</td>
      </tr>
      <tr>
          <td>電池 / 電源管理</td>
          <td>筆電專屬功能</td>
      </tr>
      <tr>
          <td>日常使用效能</td>
          <td>只有在實機跑一段時間才能評估「能不能當主力」</td>
      </tr>
  </tbody>
</table>
<h2 id="務實的-vm-使用策略">務實的 VM 使用策略</h2>
<p>VM 階段的目標是「把配置寫好、驗證邏輯、確認 bootstrap script 能跑」，不是「體驗 Hyprland 好不好用」。具體做法：</p>
<ol>
<li>在 VM 中完成 Arch Linux 安裝 + Hyprland 套件安裝（怎麼把那台 Linux 從 ISO 裝起來、安裝程式選項怎麼判讀、裝完怎麼驗工具與連入，見 <a href="/blog/linux/install/" data-link-title="Linux 安裝與機器初始化" data-link-desc="在 VM 或新機器從零裝好 Linux、判讀安裝程式選項、驗證最小系統、或要從外部連入跑 bootstrap 時回來讀">Linux 安裝與機器初始化</a>）</li>
<li>關閉所有視覺效果（blur / animation / shadow）</li>
<li>寫好完整的 Hyprland 配置（keybind / rules / workspace / autostart）</li>
<li>寫好 waybar / wofi / mako 配置</li>
<li>測試 stow 部署流程（從 dotfile repo clone → stow → 配置生效）</li>
<li>測試 bootstrap script（install.sh 從零到完整桌面）</li>
<li>把驗證過的配置 commit 進 dotfile repo</li>
</ol>
<p>到實機時，clone dotfile repo → 跑 install.sh → 補上硬體相關設定（monitor、GPU 驅動、觸控板）→ 打開視覺效果。VM 階段的工作在實機上幾乎不用重做。</p>
]]></content:encoded></item><item><title>模組四：視窗管理與平鋪式工作流</title><link>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/04-window-management/</guid><description>&lt;p>視窗管理器（window manager, WM）負責決定螢幕上的視窗怎麼排列、怎麼切換、怎麼調整大小。每個桌面環境都有一個 WM，差別在於它是讓你用滑鼠自己拖，還是按規則自動幫你排好。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/floating-vs-tiling/" data-link-title="浮動式 vs 平鋪式視窗管理" data-link-desc="在手動貼齊（Rectangle）跟自動平鋪之間猶豫、或想評估自己的工作型態適不適合平鋪式 WM 時回來讀">浮動式 vs 平鋪式&lt;/a>&lt;/td>
 &lt;td>兩種視窗管理模式的差異、手動貼齊 vs 自動平鋪的三層差距、適用判讀&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/macos-window-tools/" data-link-title="macOS 視窗管理工具鏈" data-link-desc="macOS 上想用鍵盤管理視窗、不確定該用哪個工具時回來讀">macOS 視窗管理工具鏈&lt;/a>&lt;/td>
 &lt;td>Rectangle / Amethyst / AeroSpace / yabai 選型判讀與配置範例&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/linux-tiling-wm/" data-link-title="Linux Tiling WM 生態" data-link-desc="要在 Linux 上選 tiling WM（i3/sway/Hyprland/bspwm）或理解 Wayland vs X11 差異時回來讀">Linux Tiling WM 生態&lt;/a>&lt;/td>
 &lt;td>主流 tiling WM 比較（i3/sway/Hyprland/bspwm/dwm）、多螢幕處理、dotfile 中的角色&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/wayland-explainer/" data-link-title="Wayland 顯示協議：為什麼 Hyprland 不跑在 X11 上" data-link-desc="想理解 Hyprland 底層的圖形架構、Wayland 跟 X11 的差異、XWayland 相容層、以及 2026 年 Wayland 已經是主流這件事時回來讀">Wayland 顯示協議&lt;/a>&lt;/td>
 &lt;td>Wayland 架構、跟 X11 的差異、XWayland 相容層、2026 採用現況、為什麼 tiling WM 選 Wayland&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>macOS 讀者的主線是前兩篇（浮動 vs 平鋪、macOS 工具鏈）。Linux Tiling WM 生態和 Wayland 顯示協議是想在 VM 或實機上體驗 Linux 桌面的選讀——macOS 上用 AeroSpace 或 yabai 的讀者可以直接跳到&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步與 Bootstrap&lt;/a>。&lt;/p>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置&lt;/a>：Hyprland 的完整配置實務&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計&lt;/a>：compositor 之上的狀態列、啟動器、通知、配色系統&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構&lt;/a>：WM 配置怎麼進 dotfile repo&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>：跨機器搬移時硬體相關設定怎麼處理&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>視窗管理器（window manager, WM）負責決定螢幕上的視窗怎麼排列、怎麼切換、怎麼調整大小。每個桌面環境都有一個 WM，差別在於它是讓你用滑鼠自己拖，還是按規則自動幫你排好。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/floating-vs-tiling/" data-link-title="浮動式 vs 平鋪式視窗管理" data-link-desc="在手動貼齊（Rectangle）跟自動平鋪之間猶豫、或想評估自己的工作型態適不適合平鋪式 WM 時回來讀">浮動式 vs 平鋪式</a></td>
          <td>兩種視窗管理模式的差異、手動貼齊 vs 自動平鋪的三層差距、適用判讀</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/macos-window-tools/" data-link-title="macOS 視窗管理工具鏈" data-link-desc="macOS 上想用鍵盤管理視窗、不確定該用哪個工具時回來讀">macOS 視窗管理工具鏈</a></td>
          <td>Rectangle / Amethyst / AeroSpace / yabai 選型判讀與配置範例</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/linux-tiling-wm/" data-link-title="Linux Tiling WM 生態" data-link-desc="要在 Linux 上選 tiling WM（i3/sway/Hyprland/bspwm）或理解 Wayland vs X11 差異時回來讀">Linux Tiling WM 生態</a></td>
          <td>主流 tiling WM 比較（i3/sway/Hyprland/bspwm/dwm）、多螢幕處理、dotfile 中的角色</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/04-window-management/wayland-explainer/" data-link-title="Wayland 顯示協議：為什麼 Hyprland 不跑在 X11 上" data-link-desc="想理解 Hyprland 底層的圖形架構、Wayland 跟 X11 的差異、XWayland 相容層、以及 2026 年 Wayland 已經是主流這件事時回來讀">Wayland 顯示協議</a></td>
          <td>Wayland 架構、跟 X11 的差異、XWayland 相容層、2026 採用現況、為什麼 tiling WM 選 Wayland</td>
      </tr>
  </tbody>
</table>
<p>macOS 讀者的主線是前兩篇（浮動 vs 平鋪、macOS 工具鏈）。Linux Tiling WM 生態和 Wayland 顯示協議是想在 VM 或實機上體驗 Linux 桌面的選讀——macOS 上用 AeroSpace 或 yabai 的讀者可以直接跳到<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步與 Bootstrap</a>。</p>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置</a>：Hyprland 的完整配置實務</li>
<li>→ <a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a>：compositor 之上的狀態列、啟動器、通知、配色系統</li>
<li>→ <a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a>：WM 配置怎麼進 dotfile repo</li>
<li>→ <a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>：跨機器搬移時硬體相關設定怎麼處理</li>
</ul>
]]></content:encoded></item><item><title>Caelestia 配置</title><link>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-configuration/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-configuration/</guid><description>&lt;p>&lt;code>~/.config/caelestia/&lt;/code> 下有兩類檔案：使用者層的 JSON 配置（&lt;code>shell.json&lt;/code>、&lt;code>cli.json&lt;/code>）控制功能和外觀，token 層（&lt;code>shell-tokens.json&lt;/code>）控制細部視覺數值——間距、圓角、字型大小、動畫曲線。&lt;code>shell.json&lt;/code> 修改後 Quickshell 自動 reload、不需要重啟（VM 實測：跑著的實例會即時吃到檔案變更、驗證後重新序列化回檔案）。配色（scheme）的熱套用是另一個獨立的 watcher、有啟動時序前提，見下方配色段。&lt;/p>
&lt;h2 id="配置檔路徑">配置檔路徑&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>路徑&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/shell.json&lt;/code>&lt;/td>
 &lt;td>主配置（使用者自建，不會自動產生）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/shell-tokens.json&lt;/code>&lt;/td>
 &lt;td>進階視覺 token（rounding、spacing、padding、字型、動畫）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/monitors/&amp;lt;name&amp;gt;/shell.json&lt;/code>&lt;/td>
 &lt;td>每螢幕覆寫（螢幕名稱用 &lt;code>hyprctl monitors&lt;/code> 查）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/monitors/&amp;lt;name&amp;gt;/shell-tokens.json&lt;/code>&lt;/td>
 &lt;td>每螢幕 token 覆寫&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/hypr-user.lua&lt;/code>&lt;/td>
 &lt;td>自定義 Hyprland 設定（Lua 格式）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/hypr-vars.lua&lt;/code>&lt;/td>
 &lt;td>Hyprland 變數覆寫&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/cli.json&lt;/code>&lt;/td>
 &lt;td>CLI 工具配置（主題目標、icon theme、workspace toggle）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.config/caelestia/templates/&lt;/code>&lt;/td>
 &lt;td>自定義色彩模板，語法 &lt;code>{{ primary.hex }}&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/.face&lt;/code>&lt;/td>
 &lt;td>個人頭像（Dashboard 用）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>~/Pictures/Wallpapers&lt;/code>&lt;/td>
 &lt;td>預設桌布目錄（可在 paths section 改）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;code>shell.json&lt;/code> 不會在安裝時自動產生——預設行為由 Caelestia 內建值決定。你只需要建立這個檔案、寫入你想覆寫的 section。未寫的 section 使用預設值。&lt;/p>
&lt;h2 id="shelljson-結構">shell.json 結構&lt;/h2>
&lt;p>shell.json 的 top-level section 對應桌面 shell 的各個元件和全域設定：&lt;/p>
&lt;h3 id="全域設定">全域設定&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Section&lt;/th>
 &lt;th>控制什麼&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>enabled&lt;/code>&lt;/td>
 &lt;td>各元件的啟用 / 停用主開關&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>appearance&lt;/code>&lt;/td>
 &lt;td>全域視覺：deformScale、rounding、spacing、padding、字型、動畫、透明度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>general&lt;/code>&lt;/td>
 &lt;td>Logo 圖片路徑、app 路徑、閒置逾時秒數、低電量警告門檻&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>services&lt;/code>&lt;/td>
 &lt;td>天氣服務、時間格式、GPU 類型（AMD/NVIDIA/Intel）、音訊後端&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>paths&lt;/code>&lt;/td>
 &lt;td>桌布目錄、歌詞目錄、assets 目錄的自定義路徑&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="元件設定">元件設定&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Section&lt;/th>
 &lt;th>控制什麼&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>bar&lt;/code>&lt;/td>
 &lt;td>狀態列：persistent toggle、工作區顯示、active window、tray、時鐘&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>background&lt;/code>&lt;/td>
 &lt;td>桌布設定、桌面時鐘、音訊視覺化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>dashboard&lt;/code>&lt;/td>
 &lt;td>媒體播放器、效能指標（CPU/GPU/RAM）、天氣&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>launcher&lt;/code>&lt;/td>
 &lt;td>應用程式搜尋、動作列表、桌布選擇&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>lock&lt;/code>&lt;/td>
 &lt;td>鎖屏：指紋認證開關、logo 重新配色&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>notifs&lt;/code>&lt;/td>
 &lt;td>通知：過期時間、分組邏輯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>osd&lt;/code>&lt;/td>
 &lt;td>音量 / 亮度變更的螢幕顯示&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>session&lt;/code>&lt;/td>
 &lt;td>登出 / 關機 / 重啟選單&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>sidebar&lt;/code>&lt;/td>
 &lt;td>hover 行為、快速開關（WiFi、藍牙、暗色模式）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>nexus&lt;/code>&lt;/td>
 &lt;td>設定介面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>utilities&lt;/code>&lt;/td>
 &lt;td>Toast 通知、VPN 開關、其他快速操作&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>border&lt;/code>&lt;/td>
 &lt;td>視窗邊框：thickness、rounding、smoothing&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="範例常見客製化">範例：常見客製化&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;bar&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;persistent&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;workspaces&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;shown&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">9&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;clock&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;%H:%M&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;notifs&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;expiration&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">8000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;paths&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;wallpapers&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;~/Pictures/MyWallpapers&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;services&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;gpu&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;amd&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;time&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;format24h&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">true&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Key 要對著實際版本驗&lt;/strong>：caelestia-shell 啟動時會改寫 shell.json、丟掉 schema 不認得的 key、只留有效子集（VM 實測 2.1.0：&lt;code>bar.clock.format&lt;/code>、&lt;code>notifs.expiration&lt;/code>、&lt;code>services.*&lt;/code> 都被丟掉、只有 &lt;code>bar.persistent&lt;/code> 與 &lt;code>workspaces.shown&lt;/code> 留下）。寫覆寫檔後啟動 shell、再回頭看檔案剩什麼，就是這個版本的有效 schema——被丟掉的 key 代表該功能在這版不存在或搬了位置，去 token 或別的 section 找。schema 的權威來源是安裝進來的 &lt;code>/usr/lib/qt6/qml/Caelestia/Config/caelestia-config.qmltypes&lt;/code>（config 編在 C++ plugin 裡、QML 原始碼查不到）。&lt;/p>
&lt;p>一個經 2.1.0 驗證的實用例——idle 鎖屏時間（預設 3 分鐘鎖、10 分鐘關螢幕）延長到 2 小時：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;general&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;idle&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;timeouts&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;timeout&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">7200&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nt">&amp;#34;idleAction&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;lock&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="token-系統">Token 系統&lt;/h2>
&lt;p>&lt;code>shell-tokens.json&lt;/code> 控制的是比 &lt;code>shell.json&lt;/code> 更細粒度的視覺數值：&lt;/p></description><content:encoded><![CDATA[<p><code>~/.config/caelestia/</code> 下有兩類檔案：使用者層的 JSON 配置（<code>shell.json</code>、<code>cli.json</code>）控制功能和外觀，token 層（<code>shell-tokens.json</code>）控制細部視覺數值——間距、圓角、字型大小、動畫曲線。<code>shell.json</code> 修改後 Quickshell 自動 reload、不需要重啟（VM 實測：跑著的實例會即時吃到檔案變更、驗證後重新序列化回檔案）。配色（scheme）的熱套用是另一個獨立的 watcher、有啟動時序前提，見下方配色段。</p>
<h2 id="配置檔路徑">配置檔路徑</h2>
<table>
  <thead>
      <tr>
          <th>路徑</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>~/.config/caelestia/shell.json</code></td>
          <td>主配置（使用者自建，不會自動產生）</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/shell-tokens.json</code></td>
          <td>進階視覺 token（rounding、spacing、padding、字型、動畫）</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/monitors/&lt;name&gt;/shell.json</code></td>
          <td>每螢幕覆寫（螢幕名稱用 <code>hyprctl monitors</code> 查）</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/monitors/&lt;name&gt;/shell-tokens.json</code></td>
          <td>每螢幕 token 覆寫</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/hypr-user.lua</code></td>
          <td>自定義 Hyprland 設定（Lua 格式）</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/hypr-vars.lua</code></td>
          <td>Hyprland 變數覆寫</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/cli.json</code></td>
          <td>CLI 工具配置（主題目標、icon theme、workspace toggle）</td>
      </tr>
      <tr>
          <td><code>~/.config/caelestia/templates/</code></td>
          <td>自定義色彩模板，語法 <code>{{ primary.hex }}</code></td>
      </tr>
      <tr>
          <td><code>~/.face</code></td>
          <td>個人頭像（Dashboard 用）</td>
      </tr>
      <tr>
          <td><code>~/Pictures/Wallpapers</code></td>
          <td>預設桌布目錄（可在 paths section 改）</td>
      </tr>
  </tbody>
</table>
<p><code>shell.json</code> 不會在安裝時自動產生——預設行為由 Caelestia 內建值決定。你只需要建立這個檔案、寫入你想覆寫的 section。未寫的 section 使用預設值。</p>
<h2 id="shelljson-結構">shell.json 結構</h2>
<p>shell.json 的 top-level section 對應桌面 shell 的各個元件和全域設定：</p>
<h3 id="全域設定">全域設定</h3>
<table>
  <thead>
      <tr>
          <th>Section</th>
          <th>控制什麼</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>enabled</code></td>
          <td>各元件的啟用 / 停用主開關</td>
      </tr>
      <tr>
          <td><code>appearance</code></td>
          <td>全域視覺：deformScale、rounding、spacing、padding、字型、動畫、透明度</td>
      </tr>
      <tr>
          <td><code>general</code></td>
          <td>Logo 圖片路徑、app 路徑、閒置逾時秒數、低電量警告門檻</td>
      </tr>
      <tr>
          <td><code>services</code></td>
          <td>天氣服務、時間格式、GPU 類型（AMD/NVIDIA/Intel）、音訊後端</td>
      </tr>
      <tr>
          <td><code>paths</code></td>
          <td>桌布目錄、歌詞目錄、assets 目錄的自定義路徑</td>
      </tr>
  </tbody>
</table>
<h3 id="元件設定">元件設定</h3>
<table>
  <thead>
      <tr>
          <th>Section</th>
          <th>控制什麼</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>bar</code></td>
          <td>狀態列：persistent toggle、工作區顯示、active window、tray、時鐘</td>
      </tr>
      <tr>
          <td><code>background</code></td>
          <td>桌布設定、桌面時鐘、音訊視覺化</td>
      </tr>
      <tr>
          <td><code>dashboard</code></td>
          <td>媒體播放器、效能指標（CPU/GPU/RAM）、天氣</td>
      </tr>
      <tr>
          <td><code>launcher</code></td>
          <td>應用程式搜尋、動作列表、桌布選擇</td>
      </tr>
      <tr>
          <td><code>lock</code></td>
          <td>鎖屏：指紋認證開關、logo 重新配色</td>
      </tr>
      <tr>
          <td><code>notifs</code></td>
          <td>通知：過期時間、分組邏輯</td>
      </tr>
      <tr>
          <td><code>osd</code></td>
          <td>音量 / 亮度變更的螢幕顯示</td>
      </tr>
      <tr>
          <td><code>session</code></td>
          <td>登出 / 關機 / 重啟選單</td>
      </tr>
      <tr>
          <td><code>sidebar</code></td>
          <td>hover 行為、快速開關（WiFi、藍牙、暗色模式）</td>
      </tr>
      <tr>
          <td><code>nexus</code></td>
          <td>設定介面</td>
      </tr>
      <tr>
          <td><code>utilities</code></td>
          <td>Toast 通知、VPN 開關、其他快速操作</td>
      </tr>
      <tr>
          <td><code>border</code></td>
          <td>視窗邊框：thickness、rounding、smoothing</td>
      </tr>
  </tbody>
</table>
<h3 id="範例常見客製化">範例：常見客製化</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="nt">&#34;bar&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="nt">&#34;persistent&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="nt">&#34;workspaces&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">            <span class="nt">&#34;shown&#34;</span><span class="p">:</span> <span class="mi">9</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="nt">&#34;clock&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;%H:%M&#34;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nt">&#34;notifs&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="nt">&#34;expiration&#34;</span><span class="p">:</span> <span class="mi">8000</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="nt">&#34;paths&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nt">&#34;wallpapers&#34;</span><span class="p">:</span> <span class="s2">&#34;~/Pictures/MyWallpapers&#34;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nt">&#34;services&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="nt">&#34;gpu&#34;</span><span class="p">:</span> <span class="s2">&#34;amd&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="nt">&#34;time&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">            <span class="nt">&#34;format24h&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>Key 要對著實際版本驗</strong>：caelestia-shell 啟動時會改寫 shell.json、丟掉 schema 不認得的 key、只留有效子集（VM 實測 2.1.0：<code>bar.clock.format</code>、<code>notifs.expiration</code>、<code>services.*</code> 都被丟掉、只有 <code>bar.persistent</code> 與 <code>workspaces.shown</code> 留下）。寫覆寫檔後啟動 shell、再回頭看檔案剩什麼，就是這個版本的有效 schema——被丟掉的 key 代表該功能在這版不存在或搬了位置，去 token 或別的 section 找。schema 的權威來源是安裝進來的 <code>/usr/lib/qt6/qml/Caelestia/Config/caelestia-config.qmltypes</code>（config 編在 C++ plugin 裡、QML 原始碼查不到）。</p>
<p>一個經 2.1.0 驗證的實用例——idle 鎖屏時間（預設 3 分鐘鎖、10 分鐘關螢幕）延長到 2 小時：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="nt">&#34;general&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="nt">&#34;idle&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">            <span class="nt">&#34;timeouts&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">                    <span class="nt">&#34;timeout&#34;</span><span class="p">:</span> <span class="mi">7200</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">                    <span class="nt">&#34;idleAction&#34;</span><span class="p">:</span> <span class="s2">&#34;lock&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">                <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            <span class="p">]</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="token-系統">Token 系統</h2>
<p><code>shell-tokens.json</code> 控制的是比 <code>shell.json</code> 更細粒度的視覺數值：</p>
<ul>
<li>每個元件的 rounding（圓角半徑）</li>
<li>各區域的 spacing 和 padding</li>
<li>各類別文字的 font size</li>
<li>動畫的 duration 和 easing curve</li>
<li>元件的固定尺寸</li>
</ul>
<p>官方的說法是：「Do NOT change any of these options if you do not know what you are doing.」這個警告的實際意義是——token 之間有隱含的依賴關係（某個 padding 值跟某個 rounding 值配合才好看），隨意改一個可能讓整體視覺走樣。而且 token 的名稱和結構可能在版本更新時變動，沒有向後相容承諾。</p>
<p>務實的做法是先不動 token，用 <code>shell.json</code> 做功能層面的客製化。等到你有明確的視覺需求（例如想把所有圓角改更大），再查 token 文件做精確調整。</p>
<h2 id="自定義-hyprland-設定">自定義 Hyprland 設定</h2>
<p>Caelestia 管理自己的 Hyprland 配置。你的自訂設定放在 <code>hypr-user.lua</code>（不是直接改 hyprland.lua）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">-- ~/.config/caelestia/hypr-user.lua</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">-- 額外的 keybind</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">hl.bind</span><span class="p">(</span><span class="s2">&#34;SUPER&#34;</span><span class="p">,</span> <span class="s2">&#34;Return&#34;</span><span class="p">,</span> <span class="s2">&#34;exec&#34;</span><span class="p">,</span> <span class="s2">&#34;foot&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">-- monitor 配置</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">monitor</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="s2">&#34;DP-1, 2560x1440@144, 0x0, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="s2">&#34;HDMI-A-1, 1920x1080@60, 2560x0, 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">})</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">-- 額外的 window rules</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="n">hl.config</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">windowrule</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="s2">&#34;workspace 8, class:^(firefox)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">})</span></span></span></code></pre></div><p><strong>Hyprland Lua 格式</strong>：Caelestia 已經遷移到 Lua 配置（對應 Hyprland v0.55+ 的格式變更）。如果你手上有舊的 <code>hypr-user.conf</code>，需要轉換成 Lua。轉換工具：<code>hyprlang2lua</code>（Go）或 <code>hyprconf2lua</code>（Python pip）。</p>
<h2 id="配色與主題">配色與主題</h2>
<p>Caelestia 的配色系統用 Material Design 3 的動態取色——從桌布圖片自動提取色彩，產生一套 primary / secondary / surface / error 色系，套用到所有元件。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 設定桌布並觸發動態取色</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">caelestia wallpaper -f ~/Pictures/Wallpapers/mountain.jpg
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 手動切換配色方案</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">caelestia scheme <span class="nb">set</span> -n dynamic    <span class="c1"># 動態（從桌布取）</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">caelestia scheme <span class="nb">set</span> -n catppuccin <span class="c1"># 如果有對應 template</span></span></span></code></pre></div><p>自定義配色模板放在 <code>~/.config/caelestia/templates/</code>，用 <code>{{ primary.hex }}</code>、<code>{{ surface.hex }}</code> 等變數。Caelestia 會在切換桌布時用新的色系填入這些變數，產生對應的配置檔。</p>
<p><strong>Scheme 熱套用的啟動時序前提</strong>（兩台 VM 各實測一次、行為一致）：shell 對 <code>~/.local/state/caelestia/scheme.json</code> 的 file watcher 只在<strong>啟動當下檔案已存在</strong>時建立。首次安裝、從未 <code>scheme set</code> 過的機器上，第一個 shell 實例啟動時 state 檔還不存在——之後下 <code>scheme set</code> 會寫入 state、CLI 回報也正確，但跑著的 UI 不變色。修法：<code>scheme set</code> 建好 state 檔後重啟一次 shell；那之後同一實例的配色切換都即時生效、不用再重啟。</p>
<p><strong>重啟 shell 用 <code>pkill -x qs</code></strong>：<code>caelestia shell -k</code> 可能靜默失敗（實測 CLI 1.1.1：指令回傳正常但 process 沒死、新起的實例偵測到舊實例就自行退出——看起來重啟了、實際同一個實例一直活著）。驗證重啟是否真的發生，比對 <code>ps -o pid,lstart -p $(pgrep -x qs)</code> 的 pid 與起始時間，別只看指令沒報錯。process 名是 <code>qs</code>（<code>/usr/bin/qs</code> 是 quickshell 的 symlink）、<code>pgrep caelestia</code> 找不到它。</p>
<h2 id="不要改-aur-安裝的檔案">不要改 AUR 安裝的檔案</h2>
<p>AUR package（<code>caelestia-shell</code>）安裝的檔案在系統路徑下，更新時會被覆蓋。所有客製化都應該放在 <code>~/.config/caelestia/</code>，Caelestia 會優先讀取使用者路徑的配置，沒有的才 fallback 到系統預設。</p>
<h2 id="已知問題">已知問題</h2>
<p><strong>Config 靜默破壞</strong>：Caelestia 的 token 名稱和配置結構可能在版本更新時變動，不會事先通知。更新後如果 shell 行為異常，先檢查 <code>shell-tokens.json</code> 裡的 key 是否還存在。</p>
<p><strong>Notification backlog</strong>：Shell 可能因為積累大量通知而變卡。清除：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">caelestia shell notifs clear</span></span></code></pre></div><p><strong>quickshell-git 必須</strong>：穩定版的 quickshell 缺少 Caelestia 需要的 API。如果裝了穩定版，shell 會啟動失敗或功能不完整。確認用的是 <code>quickshell-git</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">pacman -Q quickshell-git</span></span></code></pre></div><p><strong>工作區切換卡頓</strong>：在某些 GPU / 驅動組合下報告過隨機卡頓。排查方向：關閉 VRR（<code>vrr = 0</code>）、減少 blur passes、檢查 GPU 驅動版本。</p>
<h2 id="dotfile-repo-結構對應">Dotfile Repo 結構對應</h2>
<p>Caelestia 的配置只追蹤覆寫用的檔案（<code>shell.json</code>、<code>cli.json</code>、<code>hypr-user.lua</code>），AUR package 安裝的原始檔案不進 repo：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln"> 1</span><span class="cl">~/dotfiles/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">└── caelestia/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    └── .config/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        └── caelestia/
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">            ├── shell.json
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            ├── shell-tokens.json   # 如果有自訂
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            ├── cli.json
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            ├── hypr-user.lua
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            ├── hypr-vars.lua       # 如果有自訂
</span></span><span class="line"><span class="ln">10</span><span class="cl">            └── templates/          # 如果有自訂配色模板</span></span></code></pre></div><p>monitor 專屬的覆寫（<code>monitors/&lt;name&gt;/</code>）是硬體相關的，跟 <a href="/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/" data-link-title="Hyprland 核心配置" data-link-desc="Hyprland 的配置檔該怎麼組織、monitor 怎麼設定、keybind 怎麼設計、輸入裝置和環境變數怎麼配時回來讀">Hyprland 的 monitor 設定</a>一樣，可能需要排除在 Git 外或用 template/local 機制處理。</p>
<p><strong>Runtime 寫入會穿透 stow 的目錄 symlink 弄髒 repo</strong>：用 stow 部署時 <code>~/.config/caelestia</code> 常是折疊的目錄 symlink、指向 repo 裡的 package 目錄，而 caelestia-shell 會在執行期往自己的 config 目錄寫（啟動時建 <code>monitors/</code>、改寫 <code>shell.json</code>）——這些寫入直接落進 dotfiles repo、<code>git status</code> 變髒（btop 往 <code>themes/</code> 寫也是同一型）。配套：repo 的 <code>.gitignore</code> 把「app 會自己寫的路徑」列入（<code>caelestia/.config/caelestia/monitors/</code> 等），shell.json 被改寫後 diff 一下、把有效 schema 的版本 commit 回去。</p>
<h2 id="vm-測試-vs-實機測試">VM 測試 vs 實機測試</h2>
<blockquote>
<p><strong>[VM 可測試]</strong> shell.json 配置語法、各 section 的效果（bar 模組顯示、launcher 搜尋行為、通知過期邏輯）、CLI 指令執行、hypr-user.lua 載入、配色方案切換指令。</p></blockquote>
<blockquote>
<p><strong>[需實機測試]</strong> token 微調的視覺效果（間距和圓角的差異在軟體渲染下難以判讀）。動畫流暢度、blur 效能、動態取色品質、多螢幕佈局、日常穩定性等視覺與效能項目見 <a href="/blog/linux/dotfile/06-rice-design/caelestia-installation/" data-link-title="Caelestia 安裝" data-link-desc="要在 Arch Linux 上安裝 Caelestia 桌面 shell 時回來讀">Caelestia 安裝</a>的對應段落。</p></blockquote>
]]></content:encoded></item><item><title>模組五：Hyprland 配置</title><link>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/</guid><description>&lt;p>Hyprland 是 Wayland 上的動態平鋪式 compositor，在 tiling WM 裡少見地重視視覺效果——流暢的視窗動畫、圓角、模糊、漸層邊框。這個模組教它的配置檔怎麼寫、核心概念是什麼、以及常見的設定場景。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">視窗管理與平鋪式工作流&lt;/a>講了平鋪概念和選型，這裡直接進入 Hyprland 的配置實務。之後的 VM 實測專案會用這些配置做實際安裝和驗證。&lt;/p>
&lt;p>macOS 讀者如果不打算在 Linux 或 VM 上跑 Hyprland，可以跳到&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步、Bootstrap 與環境重建&lt;/a>——bootstrap script 和 secret 管理的概念對 macOS 同樣適用。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-installation/" data-link-title="Hyprland 安裝與環境建置" data-link-desc="要在 Arch Linux 上從零安裝 Hyprland 桌面環境時回來讀">安裝與環境建置&lt;/a>&lt;/td>
 &lt;td>Arch Linux 套件、GPU 驅動（AMD/Intel/NVIDIA 含完整設定）、配套工具、登入管理器、首次啟動除錯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/" data-link-title="Hyprland 核心配置" data-link-desc="Hyprland 的配置檔該怎麼組織、monitor 怎麼設定、keybind 怎麼設計、輸入裝置和環境變數怎麼配時回來讀">Hyprland 核心配置&lt;/a>&lt;/td>
 &lt;td>配置檔位置與模組化拆分、monitor 設定、keybind 設計（含完整配置範例）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/workspace-rules-appearance/" data-link-title="Workspace、Window Rules 與外觀" data-link-desc="Hyprland 的 workspace 綁定螢幕、window rules 設定浮動例外、外觀動畫調教、layout 選型、autostart 和 plugin 管理時回來讀">Workspace、Window Rules 與外觀&lt;/a>&lt;/td>
 &lt;td>workspace 設定、window rules、外觀設定、兩種平鋪分割演算法（dwindle / master）、autostart、plugin、穩定性維護&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/" data-link-title="Hyprland VM 環境設定與測試矩陣" data-link-desc="要在 VM 裡測試 Hyprland 配置、或判斷某個設定該在 VM 還是實機驗證時回來讀">VM 環境設定與測試矩陣&lt;/a>&lt;/td>
 &lt;td>UTM/QEMU 設定、VM 環境變數、效能預期、VM 可測試 vs 需實機測試的完整矩陣&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">模組四：視窗管理與自動平鋪&lt;/a>：平鋪式 WM 的概念和選型&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計&lt;/a>：compositor 之上的狀態列、啟動器、通知、配色系統&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七：桌面環境維護與故障排除&lt;/a>：Hyprland crash、GPU hang、config 寫錯時的診斷和恢復&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>：跨機器搬移時硬體相關設定怎麼處理&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/tools/gui/" data-link-title="圖形桌面工具" data-link-desc="有圖形環境時要挑桌面應用（檔案管理員、桌面環境與擴充）、需要判斷不同實作的相依足跡與桌面環境耦合、以及在最小化 WM 上加一個 GUI app 的取捨時回來讀">Linux 工具選單：圖形桌面工具&lt;/a>：在 Hyprland 下加圖形檔案管理員（Thunar / Nemo / PCManFM-Qt）的相依取捨&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Hyprland 是 Wayland 上的動態平鋪式 compositor，在 tiling WM 裡少見地重視視覺效果——流暢的視窗動畫、圓角、模糊、漸層邊框。這個模組教它的配置檔怎麼寫、核心概念是什麼、以及常見的設定場景。</p>
<p><a href="/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">視窗管理與平鋪式工作流</a>講了平鋪概念和選型，這裡直接進入 Hyprland 的配置實務。之後的 VM 實測專案會用這些配置做實際安裝和驗證。</p>
<p>macOS 讀者如果不打算在 Linux 或 VM 上跑 Hyprland，可以跳到<a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">同步、Bootstrap 與環境重建</a>——bootstrap script 和 secret 管理的概念對 macOS 同樣適用。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/05-hyprland-config/hyprland-installation/" data-link-title="Hyprland 安裝與環境建置" data-link-desc="要在 Arch Linux 上從零安裝 Hyprland 桌面環境時回來讀">安裝與環境建置</a></td>
          <td>Arch Linux 套件、GPU 驅動（AMD/Intel/NVIDIA 含完整設定）、配套工具、登入管理器、首次啟動除錯</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/05-hyprland-config/hyprland-core-config/" data-link-title="Hyprland 核心配置" data-link-desc="Hyprland 的配置檔該怎麼組織、monitor 怎麼設定、keybind 怎麼設計、輸入裝置和環境變數怎麼配時回來讀">Hyprland 核心配置</a></td>
          <td>配置檔位置與模組化拆分、monitor 設定、keybind 設計（含完整配置範例）</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/05-hyprland-config/workspace-rules-appearance/" data-link-title="Workspace、Window Rules 與外觀" data-link-desc="Hyprland 的 workspace 綁定螢幕、window rules 設定浮動例外、外觀動畫調教、layout 選型、autostart 和 plugin 管理時回來讀">Workspace、Window Rules 與外觀</a></td>
          <td>workspace 設定、window rules、外觀設定、兩種平鋪分割演算法（dwindle / master）、autostart、plugin、穩定性維護</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/05-hyprland-config/hyprland-vm-setup/" data-link-title="Hyprland VM 環境設定與測試矩陣" data-link-desc="要在 VM 裡測試 Hyprland 配置、或判斷某個設定該在 VM 還是實機驗證時回來讀">VM 環境設定與測試矩陣</a></td>
          <td>UTM/QEMU 設定、VM 環境變數、效能預期、VM 可測試 vs 需實機測試的完整矩陣</td>
      </tr>
  </tbody>
</table>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/04-window-management/" data-link-title="模組四：視窗管理與平鋪式工作流" data-link-desc="同時開多個視窗時的排列策略 — 手動貼齊跟自動平鋪的差距在哪、macOS 和 Linux 各有哪些工具、多螢幕怎麼處理、什麼情境值得從浮動切換到平鋪">模組四：視窗管理與自動平鋪</a>：平鋪式 WM 的概念和選型</li>
<li>→ <a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a>：compositor 之上的狀態列、啟動器、通知、配色系統</li>
<li>→ <a href="/blog/linux/dotfile/07-desktop-maintenance/" data-link-title="模組七：桌面環境維護與故障排除" data-link-desc="桌面凍結、compositor 掛了、或某個工具不回應時回來讀 — Linux 桌面的故障隔離模型、常見故障場景的恢復操作、日誌判讀與診斷工具">模組七：桌面環境維護與故障排除</a>：Hyprland crash、GPU hang、config 寫錯時的診斷和恢復</li>
<li>→ <a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>：跨機器搬移時硬體相關設定怎麼處理</li>
<li>→ <a href="/blog/linux/tools/gui/" data-link-title="圖形桌面工具" data-link-desc="有圖形環境時要挑桌面應用（檔案管理員、桌面環境與擴充）、需要判斷不同實作的相依足跡與桌面環境耦合、以及在最小化 WM 上加一個 GUI app 的取捨時回來讀">Linux 工具選單：圖形桌面工具</a>：在 Hyprland 下加圖形檔案管理員（Thunar / Nemo / PCManFM-Qt）的相依取捨</li>
</ul>
]]></content:encoded></item><item><title>整合式 Shell vs 手動拼裝：實測足跡、失敗半徑與選型判準</title><link>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/integrated-shell-vs-manual-assembly/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/integrated-shell-vs-manual-assembly/</guid><description>&lt;p>整合式桌面 shell 與手動拼裝，是「一個大程式包辦整個桌面」與「多個小程式各司其職、由 compositor 黏起來」兩種架構。&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&amp;#43;wofi&amp;#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia 總覽&lt;/a> 從概念層談過它的取捨（設計鎖定、穩定性風險）；這篇補上在同一台機器上實際跑過兩種之後量到的數據——資源足跡、失敗半徑、配色一致性——把「感覺整合比較方便」變成可以拿數字判斷的選型。&lt;/p>
&lt;p>這裡的數據來自一次 VM 實測：先手動拼裝一套 waybar + wofi + mako + hyprlock，再換成 Caelestia，量兩者的安裝大小、記憶體、config 結構與失敗行為。&lt;/p>
&lt;h2 id="資源足跡差約一個數量級">資源足跡：差約一個數量級&lt;/h2>
&lt;p>整合式 shell 把整個桌面畫在一個程式裡，這個程式通常是重量級的 UI runtime。Caelestia 建在 Quickshell（Qt6/QML）上，實測安裝足跡如下：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>項目&lt;/th>
 &lt;th>整合式（Caelestia）&lt;/th>
 &lt;th>手動拼裝（waybar+wofi+mako+hyprlock）&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>安裝大小&lt;/td>
 &lt;td>約 230 MB（Quickshell 佔 213 MB）&lt;/td>
 &lt;td>約 4.5 MB（waybar 3 MB，其餘 KB 級）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>執行記憶體 RSS&lt;/td>
 &lt;td>單一 &lt;code>qs&lt;/code> 程式約 400 MB&lt;/td>
 &lt;td>waybar 約 53 MB + 通知/啟動器（小）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>差距的來源是 Quickshell 這個 UI 框架——那 213 MB 是 &lt;code>quickshell&lt;/code> 套件本身的安裝大小（一個建在 Qt6 上的 QML shell runtime），不是 Caelestia 的功能程式碼；而且 Qt6 的函式庫（&lt;code>qt6-declarative&lt;/code>、&lt;code>qt6-base&lt;/code> 等）還是它之上的額外相依，沒算進這 213 MB。手動拼裝的 waybar、wofi、mako 都是輕量的 wlroots/GTK 程式，加起來還不到 5 MB。&lt;/p>
&lt;p>這一軸在資源受限的機器上才會咬人：舊筆電、記憶體小的 VPS、或你本來就想把桌面壓到最輕。在一台記憶體充裕的桌機上，400 MB 對 60 MB 的差別多半感覺不到；在一台 2 GB RAM 的機器上，這就是「桌面吃掉五分之一記憶體」跟「幾乎不佔」的差別。&lt;/p>
&lt;h2 id="失敗半徑單點-vs-各自獨立">失敗半徑：單點 vs 各自獨立&lt;/h2>
&lt;p>整合式 shell 把狀態列、通知、鎖屏、啟動器畫在&lt;strong>同一個程式&lt;/strong>裡，所以這個程式崩潰時，這些東西會&lt;strong>一起消失&lt;/strong>。手動拼裝的每個元件是獨立行程，一個崩掉不影響其他——mako（通知）崩了，waybar（狀態列）還在。&lt;/p>
&lt;p>這不只是理論。這次 VM 實測就撞到一個具體案例：Caelestia 的鎖屏是由 Quickshell 主程式畫的，當這個持鎖的程式被中止時，Hyprland 依 &lt;code>ext-session-lock&lt;/code> 協議保持鎖定並顯示「lockscreen app died」的死局——狀態列、通知、鎖屏因為同源，一個環節出事就連帶整個桌面 UI。手動拼裝的 hyprlock 是獨立的鎖屏程式，它崩潰同樣會觸發那個死局，但你的狀態列與通知不會跟著沒。&lt;/p>
&lt;p>這一軸在穩定性敏感或無人值守的場景最關鍵。跑長時間無人盯著的任務時，「一個元件崩掉只損失那個元件」的隔離性，比「全部整合在一起」的一致性更值錢——因為沒人在旁邊立刻重啟。&lt;/p>
&lt;h2 id="配色一致性最容易被低估的一軸">配色一致性：最容易被低估的一軸&lt;/h2>
&lt;p>讓整個桌面配色一致，是整合式與手動拼裝差別最大、卻最常被忽略的地方。整合式 shell 因為所有元件在同一個程式裡，天生共用一套配色——Caelestia 的 dynamic scheme 從桌布抽一組 Material-3 palette，狀態列、通知、鎖屏、dashboard 全部同時套用，換張桌布整套 UI 跟著變。&lt;/p>
&lt;p>手動拼裝要達到同樣的一致，得自己解決一個跨程式的問題：每個元件用不同的設定格式與主題引擎，它們之間不會自動共享顏色。這次手動拼裝那套時就撞到這點——waybar 的 GTK CSS 引擎讀不到 Hyprland 的 &lt;code>$&lt;/code> 顏色變數，結果 waybar 的 &lt;code>style.css&lt;/code> 裡得&lt;strong>手抄一份跟 Hyprland &lt;code>colors.conf&lt;/code> 相同的 hex 色碼&lt;/strong>。換一次配色，就要在 waybar CSS、wofi CSS、mako config、hyprland colors 好幾個地方各改一遍。&lt;/p>
&lt;p>解這個手工問題的標準做法，是加一層&lt;strong>模板工具&lt;/strong>（matugen、pywal、wallust 之類）：從一張桌布或一套色票，自動生成每個元件的設定檔（例如 &lt;code>matugen/templates/rofi-colors.rasi&lt;/code> 就是給 rofi 用的顏色模板）。這等於是手動重建 Caelestia 內建的那套 dynamic theming pipeline。所以配色一致這件事的真正取捨是：Caelestia 開箱就有「換桌布全套跟著變」，手動拼裝要嘛手抄 hex、要嘛自己搭一條 templating pipeline。&lt;/p>
&lt;h2 id="config-結構">config 結構&lt;/h2>
&lt;p>配色一致的差別，也反映在 config 的形狀上。Caelestia 的使用者設定集中在一個 &lt;code>shell.json&lt;/code>（實測約 24 行就涵蓋狀態列、通知、idle 行為）。手動拼裝的設定散在各元件目錄、各用各的格式：waybar 的 &lt;code>config.jsonc&lt;/code> + &lt;code>style.css&lt;/code>、wofi 的 &lt;code>config&lt;/code> + &lt;code>style.css&lt;/code>、mako 的 &lt;code>config&lt;/code>、hypr 的數個 &lt;code>.conf&lt;/code>。集中的好處是好懂好改；散開的好處是每個元件可以獨立替換（把 waybar 換成 ironbar 不影響其他），代價是你要管更多檔案、更多格式。&lt;/p></description><content:encoded><![CDATA[<p>整合式桌面 shell 與手動拼裝，是「一個大程式包辦整個桌面」與「多個小程式各司其職、由 compositor 黏起來」兩種架構。<a href="/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&#43;wofi&#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia 總覽</a> 從概念層談過它的取捨（設計鎖定、穩定性風險）；這篇補上在同一台機器上實際跑過兩種之後量到的數據——資源足跡、失敗半徑、配色一致性——把「感覺整合比較方便」變成可以拿數字判斷的選型。</p>
<p>這裡的數據來自一次 VM 實測：先手動拼裝一套 waybar + wofi + mako + hyprlock，再換成 Caelestia，量兩者的安裝大小、記憶體、config 結構與失敗行為。</p>
<h2 id="資源足跡差約一個數量級">資源足跡：差約一個數量級</h2>
<p>整合式 shell 把整個桌面畫在一個程式裡，這個程式通常是重量級的 UI runtime。Caelestia 建在 Quickshell（Qt6/QML）上，實測安裝足跡如下：</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>整合式（Caelestia）</th>
          <th>手動拼裝（waybar+wofi+mako+hyprlock）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>安裝大小</td>
          <td>約 230 MB（Quickshell 佔 213 MB）</td>
          <td>約 4.5 MB（waybar 3 MB，其餘 KB 級）</td>
      </tr>
      <tr>
          <td>執行記憶體 RSS</td>
          <td>單一 <code>qs</code> 程式約 400 MB</td>
          <td>waybar 約 53 MB + 通知/啟動器（小）</td>
      </tr>
  </tbody>
</table>
<p>差距的來源是 Quickshell 這個 UI 框架——那 213 MB 是 <code>quickshell</code> 套件本身的安裝大小（一個建在 Qt6 上的 QML shell runtime），不是 Caelestia 的功能程式碼；而且 Qt6 的函式庫（<code>qt6-declarative</code>、<code>qt6-base</code> 等）還是它之上的額外相依，沒算進這 213 MB。手動拼裝的 waybar、wofi、mako 都是輕量的 wlroots/GTK 程式，加起來還不到 5 MB。</p>
<p>這一軸在資源受限的機器上才會咬人：舊筆電、記憶體小的 VPS、或你本來就想把桌面壓到最輕。在一台記憶體充裕的桌機上，400 MB 對 60 MB 的差別多半感覺不到；在一台 2 GB RAM 的機器上，這就是「桌面吃掉五分之一記憶體」跟「幾乎不佔」的差別。</p>
<h2 id="失敗半徑單點-vs-各自獨立">失敗半徑：單點 vs 各自獨立</h2>
<p>整合式 shell 把狀態列、通知、鎖屏、啟動器畫在<strong>同一個程式</strong>裡，所以這個程式崩潰時，這些東西會<strong>一起消失</strong>。手動拼裝的每個元件是獨立行程，一個崩掉不影響其他——mako（通知）崩了，waybar（狀態列）還在。</p>
<p>這不只是理論。這次 VM 實測就撞到一個具體案例：Caelestia 的鎖屏是由 Quickshell 主程式畫的，當這個持鎖的程式被中止時，Hyprland 依 <code>ext-session-lock</code> 協議保持鎖定並顯示「lockscreen app died」的死局——狀態列、通知、鎖屏因為同源，一個環節出事就連帶整個桌面 UI。手動拼裝的 hyprlock 是獨立的鎖屏程式，它崩潰同樣會觸發那個死局，但你的狀態列與通知不會跟著沒。</p>
<p>這一軸在穩定性敏感或無人值守的場景最關鍵。跑長時間無人盯著的任務時，「一個元件崩掉只損失那個元件」的隔離性，比「全部整合在一起」的一致性更值錢——因為沒人在旁邊立刻重啟。</p>
<h2 id="配色一致性最容易被低估的一軸">配色一致性：最容易被低估的一軸</h2>
<p>讓整個桌面配色一致，是整合式與手動拼裝差別最大、卻最常被忽略的地方。整合式 shell 因為所有元件在同一個程式裡，天生共用一套配色——Caelestia 的 dynamic scheme 從桌布抽一組 Material-3 palette，狀態列、通知、鎖屏、dashboard 全部同時套用，換張桌布整套 UI 跟著變。</p>
<p>手動拼裝要達到同樣的一致，得自己解決一個跨程式的問題：每個元件用不同的設定格式與主題引擎，它們之間不會自動共享顏色。這次手動拼裝那套時就撞到這點——waybar 的 GTK CSS 引擎讀不到 Hyprland 的 <code>$</code> 顏色變數，結果 waybar 的 <code>style.css</code> 裡得<strong>手抄一份跟 Hyprland <code>colors.conf</code> 相同的 hex 色碼</strong>。換一次配色，就要在 waybar CSS、wofi CSS、mako config、hyprland colors 好幾個地方各改一遍。</p>
<p>解這個手工問題的標準做法，是加一層<strong>模板工具</strong>（matugen、pywal、wallust 之類）：從一張桌布或一套色票，自動生成每個元件的設定檔（例如 <code>matugen/templates/rofi-colors.rasi</code> 就是給 rofi 用的顏色模板）。這等於是手動重建 Caelestia 內建的那套 dynamic theming pipeline。所以配色一致這件事的真正取捨是：Caelestia 開箱就有「換桌布全套跟著變」，手動拼裝要嘛手抄 hex、要嘛自己搭一條 templating pipeline。</p>
<h2 id="config-結構">config 結構</h2>
<p>配色一致的差別，也反映在 config 的形狀上。Caelestia 的使用者設定集中在一個 <code>shell.json</code>（實測約 24 行就涵蓋狀態列、通知、idle 行為）。手動拼裝的設定散在各元件目錄、各用各的格式：waybar 的 <code>config.jsonc</code> + <code>style.css</code>、wofi 的 <code>config</code> + <code>style.css</code>、mako 的 <code>config</code>、hypr 的數個 <code>.conf</code>。集中的好處是好懂好改；散開的好處是每個元件可以獨立替換（把 waybar 換成 ironbar 不影響其他），代價是你要管更多檔案、更多格式。</p>
<h2 id="選型判準">選型判準</h2>
<p>沒有一種在所有軸上都贏。依你的情境對照：</p>
<table>
  <thead>
      <tr>
          <th>你的情境</th>
          <th>偏向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>資源受限（舊機、小 RAM VPS）</td>
          <td>手動拼裝（省下那 ~340 MB 記憶體）</td>
      </tr>
      <tr>
          <td>想要開箱即用、換桌布全套變色</td>
          <td>整合式（Caelestia 的 dynamic 原生就有）</td>
      </tr>
      <tr>
          <td>穩定性敏感、無人值守</td>
          <td>手動拼裝（元件獨立、失敗半徑小）</td>
      </tr>
      <tr>
          <td>想要結構性客製（狀態列位置、換 launcher）</td>
          <td>手動拼裝（整合式的結構是 shell 決定的）</td>
      </tr>
      <tr>
          <td>想少管檔案、快速有一套設計一致的成品</td>
          <td>整合式（一個 config、一套配色）</td>
      </tr>
      <tr>
          <td>已經在跑 templating 工具（matugen/pywal）</td>
          <td>手動拼裝（你已經有一致配色的機制、少了整合式的理由）</td>
      </tr>
  </tbody>
</table>
<h3 id="重新評估的訊號tripwire">重新評估的訊號（tripwire）</h3>
<p>選了之後，出現這些訊號時值得回頭重新評估：</p>
<ul>
<li>選了整合式，卻發現一直在跟它的設計決策對抗（想改的結構它不讓你改）——你要的其實是手動拼裝的自由度。</li>
<li>選了手動拼裝，卻發現配色維護（每次改色手抄多個檔案）吃掉大量時間——該加 templating 工具，或重新考慮整合式。</li>
<li>記憶體壓力浮現（整合式的 Qt runtime 在小機器上排擠其他程式）——往手動拼裝退。</li>
<li>整合式的一次更新靜默破壞了你的自訂設定（<a href="/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&#43;wofi&#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia README 明言 config 可能無預警變動</a>）——評估這層快速移動的依賴值不值得。</li>
</ul>
<h2 id="下一步">下一步</h2>
<ul>
<li>整合式 shell 的概念定位、跟 AGS/Eww 的比較、三個 repo 的分工，見 <a href="/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&#43;wofi&#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia 總覽</a>。</li>
<li>手動拼裝那幾個元件（狀態列、啟動器、通知）各自怎麼配置，見 <a href="/blog/linux/dotfile/06-rice-design/desktop-shell-components/" data-link-title="桌面 Shell 元件：狀態列、啟動器與通知" data-link-desc="Hyprland 桌面要拼哪些元件、各元件的配置檔怎麼寫時回來讀">桌面 Shell 元件</a>。</li>
<li>配色系統本身（不管哪條路線）怎麼設計，見 <a href="/blog/linux/dotfile/06-rice-design/color-system-theming/" data-link-title="配色系統、鎖屏與 GTK 主題" data-link-desc="桌面配色散亂看起來雜、或要換主題不知道該改哪些檔案時回來讀">配色系統、鎖屏與 GTK 主題</a>。</li>
</ul>
<p>這篇的足跡數字（安裝 230 MB vs 4.5 MB、RSS ~400 MB vs ~60 MB）與 lock-died 失敗案例，來自一次在 Apple Silicon UTM VM 上實際跑過兩種桌面棧的量測。</p>
]]></content:encoded></item><item><title>模組六：桌面 Rice 設計</title><link>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/</link><pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/</guid><description>&lt;p>Rice 在 Linux 桌面社群指的是桌面視覺客製化——把系統外觀調教成個人化的美學呈現。這個詞源自汽車改裝文化（Race Inspired Cosmetic Enhancements 的逆向縮寫），在 Linux 圈已轉為中性的圈內用語，r/unixporn 社群就是圍繞這件事運轉的。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">Hyprland 配置&lt;/a>教了 compositor（Wayland 下負責視窗排列和畫面合成的程式）本身的設定。這個模組處理的是 compositor 之上的「桌面 shell」層——狀態列、啟動器、通知、鎖屏、桌布、配色系統。做法有兩條路：自己從 Waybar + Wofi + Mako 等獨立工具一個個拼裝，或用 Caelestia 這類預組裝的桌面 shell 一次部署。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/desktop-shell-components/" data-link-title="桌面 Shell 元件：狀態列、啟動器與通知" data-link-desc="Hyprland 桌面要拼哪些元件、各元件的配置檔怎麼寫時回來讀">桌面 Shell 元件：狀態列、啟動器與通知&lt;/a>&lt;/td>
 &lt;td>拼裝式桌面的元件組成、Waybar 狀態列配置、Wofi/Rofi 啟動器、Mako 通知&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/color-system-theming/" data-link-title="配色系統、鎖屏與 GTK 主題" data-link-desc="桌面配色散亂看起來雜、或要換主題不知道該改哪些檔案時回來讀">配色系統、鎖屏與 GTK 主題&lt;/a>&lt;/td>
 &lt;td>Hyprlock 鎖屏配置、配色變數集中管理、GTK/Qt 主題統一、投資報酬判讀&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&amp;#43;wofi&amp;#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia 總覽&lt;/a>&lt;/td>
 &lt;td>Quickshell 框架、提供的元件、跟手動拼裝和 AGS/Eww 的 trade-off、定位認知&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-installation/" data-link-title="Caelestia 安裝" data-link-desc="要在 Arch Linux 上安裝 Caelestia 桌面 shell 時回來讀">Caelestia 安裝&lt;/a>&lt;/td>
 &lt;td>AUR 套件、CLI 安裝流程、依賴清單、登入管理器、CLI 指令、VM 測試範圍&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/caelestia-configuration/" data-link-title="Caelestia 配置" data-link-desc="安裝完 Caelestia 後要客製化設定時回來讀">Caelestia 配置&lt;/a>&lt;/td>
 &lt;td>shell.json 結構、token 系統、hypr-user.lua、動態取色、已知問題、dotfile 結構&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/integrated-shell-vs-manual-assembly/" data-link-title="整合式 Shell vs 手動拼裝：實測足跡、失敗半徑與選型判準" data-link-desc="在整合式桌面 shell（如 Caelestia）與手動拼裝 waybar&amp;#43;wofi&amp;#43;mako 之間選型、需要實測的資源足跡、失敗半徑與配色一致性數據來判斷時回來讀">整合式 Shell vs 手動拼裝：實測取捨&lt;/a>&lt;/td>
 &lt;td>兩種桌面棧的實測足跡（安裝/記憶體）、失敗半徑、配色一致性機制與選型判準&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器&lt;/a>：Nerd Font 安裝是 Waybar icon 正常顯示的前提&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置&lt;/a>：compositor 的 appearance 設定（圓角、動畫）跟 rice 的視覺層互補&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構&lt;/a>：各 rice 元件的配置檔怎麼放進 stow 結構&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock&lt;/a>：鎖屏的安全模型——殺 process 不等於解鎖&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/font-availability-at-startup/" data-link-title="字型的可用集合在 process 啟動時決定" data-link-desc="裝了字型但應用程式 / 狀態列 / 通知還是看不到、還是豆腐時回來讀">字型的可用集合在 process 啟動時決定&lt;/a>：裝了字型但狀態列 / 通知還是豆腐時的判讀&lt;/li>
&lt;li>→ &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/fontconfig/" data-link-title="fontconfig — 字型搜尋、匹配與 fallback 服務" data-link-desc="不確定 fc-list / fc-match / fc-cache 各做什麼、或 fontconfig fallback 機制怎麼運作時回來讀">fontconfig&lt;/a>：字型搜尋、匹配與 fallback 機制&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Rice 在 Linux 桌面社群指的是桌面視覺客製化——把系統外觀調教成個人化的美學呈現。這個詞源自汽車改裝文化（Race Inspired Cosmetic Enhancements 的逆向縮寫），在 Linux 圈已轉為中性的圈內用語，r/unixporn 社群就是圍繞這件事運轉的。</p>
<p><a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">Hyprland 配置</a>教了 compositor（Wayland 下負責視窗排列和畫面合成的程式）本身的設定。這個模組處理的是 compositor 之上的「桌面 shell」層——狀態列、啟動器、通知、鎖屏、桌布、配色系統。做法有兩條路：自己從 Waybar + Wofi + Mako 等獨立工具一個個拼裝，或用 Caelestia 這類預組裝的桌面 shell 一次部署。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/desktop-shell-components/" data-link-title="桌面 Shell 元件：狀態列、啟動器與通知" data-link-desc="Hyprland 桌面要拼哪些元件、各元件的配置檔怎麼寫時回來讀">桌面 Shell 元件：狀態列、啟動器與通知</a></td>
          <td>拼裝式桌面的元件組成、Waybar 狀態列配置、Wofi/Rofi 啟動器、Mako 通知</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/color-system-theming/" data-link-title="配色系統、鎖屏與 GTK 主題" data-link-desc="桌面配色散亂看起來雜、或要換主題不知道該改哪些檔案時回來讀">配色系統、鎖屏與 GTK 主題</a></td>
          <td>Hyprlock 鎖屏配置、配色變數集中管理、GTK/Qt 主題統一、投資報酬判讀</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/caelestia-overview/" data-link-title="Caelestia 總覽：預組裝的 Hyprland 桌面 Shell" data-link-desc="考慮用 Caelestia 取代手動拼裝 waybar&#43;wofi&#43;mako、或評估預組裝桌面 shell 的 trade-off 時回來讀">Caelestia 總覽</a></td>
          <td>Quickshell 框架、提供的元件、跟手動拼裝和 AGS/Eww 的 trade-off、定位認知</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/caelestia-installation/" data-link-title="Caelestia 安裝" data-link-desc="要在 Arch Linux 上安裝 Caelestia 桌面 shell 時回來讀">Caelestia 安裝</a></td>
          <td>AUR 套件、CLI 安裝流程、依賴清單、登入管理器、CLI 指令、VM 測試範圍</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/caelestia-configuration/" data-link-title="Caelestia 配置" data-link-desc="安裝完 Caelestia 後要客製化設定時回來讀">Caelestia 配置</a></td>
          <td>shell.json 結構、token 系統、hypr-user.lua、動態取色、已知問題、dotfile 結構</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/06-rice-design/integrated-shell-vs-manual-assembly/" data-link-title="整合式 Shell vs 手動拼裝：實測足跡、失敗半徑與選型判準" data-link-desc="在整合式桌面 shell（如 Caelestia）與手動拼裝 waybar&#43;wofi&#43;mako 之間選型、需要實測的資源足跡、失敗半徑與配色一致性數據來判斷時回來讀">整合式 Shell vs 手動拼裝：實測取捨</a></td>
          <td>兩種桌面棧的實測足跡（安裝/記憶體）、失敗半徑、配色一致性機制與選型判準</td>
      </tr>
  </tbody>
</table>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/linux/dotfile/03-terminal-ecosystem/" data-link-title="模組三：終端機與編輯器" data-link-desc="終端機相關工具的配置檔散落在不同位置、不確定哪些該進 dotfile repo 時回來讀">模組三：終端機與編輯器</a>：Nerd Font 安裝是 Waybar icon 正常顯示的前提</li>
<li>→ <a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置</a>：compositor 的 appearance 設定（圓角、動畫）跟 rice 的視覺層互補</li>
<li>→ <a href="/blog/linux/dotfile/01-dotfile-management/" data-link-title="模組一：管理工具與目錄結構" data-link-desc="要把散落在家目錄的配置檔集中版控時，選 bare repo、stow 還是 chezmoi、目錄該怎麼組織">模組一：管理工具與目錄結構</a>：各 rice 元件的配置檔怎麼放進 stow 結構</li>
<li>→ <a href="/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock</a>：鎖屏的安全模型——殺 process 不等於解鎖</li>
<li>→ <a href="/blog/linux/dotfile/knowledge-cards/font-availability-at-startup/" data-link-title="字型的可用集合在 process 啟動時決定" data-link-desc="裝了字型但應用程式 / 狀態列 / 通知還是看不到、還是豆腐時回來讀">字型的可用集合在 process 啟動時決定</a>：裝了字型但狀態列 / 通知還是豆腐時的判讀</li>
<li>→ <a href="/blog/linux/dotfile/knowledge-cards/fontconfig/" data-link-title="fontconfig — 字型搜尋、匹配與 fallback 服務" data-link-desc="不確定 fc-list / fc-match / fc-cache 各做什麼、或 fontconfig fallback 機制怎麼運作時回來讀">fontconfig</a>：字型搜尋、匹配與 fallback 機制</li>
</ul>
]]></content:encoded></item><item><title>模組七：桌面環境維護與故障排除</title><link>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/</link><pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/</guid><description>&lt;p>模組零到六教的是怎麼建立桌面環境，這個模組教的是壞了怎麼修。&lt;/p>
&lt;p>Linux 桌面環境跟 Windows 在故障模型上有根本的結構差異。Windows 的藍屏（BSOD）是核心層崩潰，整台機器停擺；Linux 桌面環境的大部分故障只影響 userspace，系統核心不受波及。理解這個隔離邊界，是判斷「當下該做什麼」的前提。&lt;/p>
&lt;p>這個模組的閱讀方式跟其他模組不同。其他模組是線性學習——從頭讀到尾建立知識。這個模組是 reference——出問題時根據症狀查對應的恢復操作。第一篇建立概念模型，第二篇按場景查操作，第三篇教怎麼看日誌找根因。&lt;/p>
&lt;h2 id="章節文章">章節文章&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>文章&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/fault-isolation-model/" data-link-title="Linux 桌面的故障隔離模型" data-link-desc="從 Windows 轉過來想知道 Linux 桌面掛了會不會整台崩潰時讀 — kernel vs userspace 隔離、compositor 是 userspace process、TTY 救生通道與其限制">Linux 桌面的故障隔離模型&lt;/a>&lt;/td>
 &lt;td>kernel vs userspace 隔離、compositor 掛了不等於系統崩潰、TTY&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作&lt;/a>&lt;/td>
 &lt;td>compositor crash、工具掛了、GPU hang、OOM、config 錯誤、suspend/resume 異常的處理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/log-reading-diagnostic-tools/" data-link-title="日誌判讀與診斷工具" data-link-desc="知道桌面出了問題但不確定原因時回來讀 — journalctl、dmesg、hyprctl、systemctl 的使用方式和常見 log pattern">日誌判讀與診斷工具&lt;/a>&lt;/td>
 &lt;td>journalctl、dmesg、hyprctl、systemctl 的使用與常見 pattern&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="跨分類引用">跨分類引用&lt;/h2>
&lt;ul>
&lt;li>-&amp;gt; &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置&lt;/a>：Hyprland 的配置結構和 hyprctl 指令&lt;/li>
&lt;li>-&amp;gt; &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計&lt;/a>：waybar / wofi / mako 等工具的配置位置&lt;/li>
&lt;li>-&amp;gt; &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建&lt;/a>：環境損壞到無法修復時的重建策略&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>模組零到六教的是怎麼建立桌面環境，這個模組教的是壞了怎麼修。</p>
<p>Linux 桌面環境跟 Windows 在故障模型上有根本的結構差異。Windows 的藍屏（BSOD）是核心層崩潰，整台機器停擺；Linux 桌面環境的大部分故障只影響 userspace，系統核心不受波及。理解這個隔離邊界，是判斷「當下該做什麼」的前提。</p>
<p>這個模組的閱讀方式跟其他模組不同。其他模組是線性學習——從頭讀到尾建立知識。這個模組是 reference——出問題時根據症狀查對應的恢復操作。第一篇建立概念模型，第二篇按場景查操作，第三篇教怎麼看日誌找根因。</p>
<h2 id="章節文章">章節文章</h2>
<table>
  <thead>
      <tr>
          <th>文章</th>
          <th>主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/linux/dotfile/07-desktop-maintenance/fault-isolation-model/" data-link-title="Linux 桌面的故障隔離模型" data-link-desc="從 Windows 轉過來想知道 Linux 桌面掛了會不會整台崩潰時讀 — kernel vs userspace 隔離、compositor 是 userspace process、TTY 救生通道與其限制">Linux 桌面的故障隔離模型</a></td>
          <td>kernel vs userspace 隔離、compositor 掛了不等於系統崩潰、TTY</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作</a></td>
          <td>compositor crash、工具掛了、GPU hang、OOM、config 錯誤、suspend/resume 異常的處理</td>
      </tr>
      <tr>
          <td><a href="/blog/linux/dotfile/07-desktop-maintenance/log-reading-diagnostic-tools/" data-link-title="日誌判讀與診斷工具" data-link-desc="知道桌面出了問題但不確定原因時回來讀 — journalctl、dmesg、hyprctl、systemctl 的使用方式和常見 log pattern">日誌判讀與診斷工具</a></td>
          <td>journalctl、dmesg、hyprctl、systemctl 的使用與常見 pattern</td>
      </tr>
  </tbody>
</table>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>-&gt; <a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">模組五：Hyprland 配置</a>：Hyprland 的配置結構和 hyprctl 指令</li>
<li>-&gt; <a href="/blog/linux/dotfile/06-rice-design/" data-link-title="模組六：桌面 Rice 設計" data-link-desc="Hyprland 桌面從能用到好看好用 — 狀態列、啟動器、通知、鎖屏、配色系統的設計與配置">模組六：桌面 Rice 設計</a>：waybar / wofi / mako 等工具的配置位置</li>
<li>-&gt; <a href="/blog/linux/dotfile/08-sync-bootstrap/" data-link-title="模組八：同步、Bootstrap 與環境重建" data-link-desc="換機器或重灌時怎麼還原工作環境 — bootstrap script 設計、套件清單管理、跨機器同步策略、secret 排除，以及 VM 快照和 dotfile 重建兩種思路的場景判讀">模組八：同步、Bootstrap 與環境重建</a>：環境損壞到無法修復時的重建策略</li>
</ul>
]]></content:encoded></item><item><title>Compositor（合成器）</title><link>https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/compositor/</link><pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/compositor/</guid><description>&lt;p>Compositor（合成器）是 Wayland 下負責把各個應用視窗的畫面合成到螢幕、同時管理視窗位置與輸入的核心程式。它一個角色承擔了舊 X11 世界裡分給多個程式的責任——畫面合成、視窗管理、輸入處理，在 Wayland 架構裡合在同一個程式。&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">Hyprland&lt;/a> 就是一個 Wayland compositor。&lt;/p>
&lt;p>跟 X11 的分工對照能看清它的定位。X11 時代，X server 負責畫面、一個獨立的 window manager 負責視窗排列，兩者透過協定溝通；Wayland 取消這個分家，compositor 直接兼任兩者。所以在 Wayland 語境裡，「compositor」和「window manager」常指同一個東西——Hyprland 既是 compositor 也是 tiling window manager。&lt;/p>
&lt;p>它跟桌面環境（desktop environment）是不同層次。桌面環境（GNOME、KDE）是一整套元件（面板、設定、通知、檔案管理），其中內含一個 compositor；而 Hyprland 這類「只有 compositor」的方案不含那圈桌面服務，面板、啟動器、鎖屏都要另外自己接。這條「整合度 vs 自己組裝」的軸線，是&lt;a href="https://tarrragon.github.io/blog/linux/tools/gui/desktop-environment-selection/" data-link-title="桌面環境選型：整合度與組裝自由度的取捨" data-link-desc="從 Windows/macOS 轉來或要挑第一個 Linux 桌面、在 GNOME / KDE / Hyprland / XFCE / Cinnamon 之間拿不定、想知道各自的定位與代價（資源、客製自由、穩定性、Wayland 支援）時回來讀">桌面環境選型&lt;/a>的主題。&lt;/p>
&lt;p>compositor 在故障排除中是一個關鍵的責任邊界，因為多個系統狀態由它持有、而非別的層：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>它握著 DRM master&lt;/strong>：直接畫到顯示裝置的獨佔權由 compositor 持有，這是為什麼 compositor 必須從實體圖形 VT 起、不能從 SSH pty 起（SSH 連線裡沒有那個 seat 與 DRM 資源），也是為什麼它跟同樣要畫到 DRM 的 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/tty/" data-link-title="TTY" data-link-desc="恢復操作提到切 TTY 但不知道 TTY 是什麼時讀 — Linux 核心直接提供的純文字終端機介面">kmscon 之類 userspace console&lt;/a> 會相衝。&lt;/li>
&lt;li>&lt;strong>它持有 session lock 狀態&lt;/strong>：Wayland 的 &lt;code>ext-session-lock&lt;/code> 鎖是 compositor 層的狀態、跟 logind 獨立，殺掉鎖屏程式 compositor 仍保持鎖定——詳見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock&lt;/a>。&lt;/li>
&lt;li>&lt;strong>它掛了、桌面才真的黑&lt;/strong>：反過來說，只要 kernel 還活著、compositor 以外的東西壞了，&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/tty/" data-link-title="TTY" data-link-desc="恢復操作提到切 TTY 但不知道 TTY 是什麼時讀 — Linux 核心直接提供的純文字終端機介面">TTY&lt;/a> 仍能登入操作。判斷「畫面黑」是 compositor 掛了、還是沒 getty、還是顯示輸出沒接，是桌面故障排除的第一個分岔。&lt;/li>
&lt;/ul>
&lt;p>相關概念：&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/tty/" data-link-title="TTY" data-link-desc="恢復操作提到切 TTY 但不知道 TTY 是什麼時讀 — Linux 核心直接提供的純文字終端機介面">TTY&lt;/a>（不依賴 compositor 的救生通道）、&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock&lt;/a>（compositor 持有的鎖定狀態）、&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/rice/" data-link-title="Rice（桌面視覺客製化）" data-link-desc="Linux 桌面文章裡看到 rice / ricing / ricer 不確定意思時回來讀">Rice&lt;/a>（在 compositor 上做視覺客製）。&lt;/p></description><content:encoded><![CDATA[<p>Compositor（合成器）是 Wayland 下負責把各個應用視窗的畫面合成到螢幕、同時管理視窗位置與輸入的核心程式。它一個角色承擔了舊 X11 世界裡分給多個程式的責任——畫面合成、視窗管理、輸入處理，在 Wayland 架構裡合在同一個程式。<a href="/blog/linux/dotfile/05-hyprland-config/" data-link-title="模組五：Hyprland 配置" data-link-desc="要在 Linux 上設定 Hyprland 平鋪式桌面時回來讀">Hyprland</a> 就是一個 Wayland compositor。</p>
<p>跟 X11 的分工對照能看清它的定位。X11 時代，X server 負責畫面、一個獨立的 window manager 負責視窗排列，兩者透過協定溝通；Wayland 取消這個分家，compositor 直接兼任兩者。所以在 Wayland 語境裡，「compositor」和「window manager」常指同一個東西——Hyprland 既是 compositor 也是 tiling window manager。</p>
<p>它跟桌面環境（desktop environment）是不同層次。桌面環境（GNOME、KDE）是一整套元件（面板、設定、通知、檔案管理），其中內含一個 compositor；而 Hyprland 這類「只有 compositor」的方案不含那圈桌面服務，面板、啟動器、鎖屏都要另外自己接。這條「整合度 vs 自己組裝」的軸線，是<a href="/blog/linux/tools/gui/desktop-environment-selection/" data-link-title="桌面環境選型：整合度與組裝自由度的取捨" data-link-desc="從 Windows/macOS 轉來或要挑第一個 Linux 桌面、在 GNOME / KDE / Hyprland / XFCE / Cinnamon 之間拿不定、想知道各自的定位與代價（資源、客製自由、穩定性、Wayland 支援）時回來讀">桌面環境選型</a>的主題。</p>
<p>compositor 在故障排除中是一個關鍵的責任邊界，因為多個系統狀態由它持有、而非別的層：</p>
<ul>
<li><strong>它握著 DRM master</strong>：直接畫到顯示裝置的獨佔權由 compositor 持有，這是為什麼 compositor 必須從實體圖形 VT 起、不能從 SSH pty 起（SSH 連線裡沒有那個 seat 與 DRM 資源），也是為什麼它跟同樣要畫到 DRM 的 <a href="/blog/linux/dotfile/knowledge-cards/tty/" data-link-title="TTY" data-link-desc="恢復操作提到切 TTY 但不知道 TTY 是什麼時讀 — Linux 核心直接提供的純文字終端機介面">kmscon 之類 userspace console</a> 會相衝。</li>
<li><strong>它持有 session lock 狀態</strong>：Wayland 的 <code>ext-session-lock</code> 鎖是 compositor 層的狀態、跟 logind 獨立，殺掉鎖屏程式 compositor 仍保持鎖定——詳見 <a href="/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock</a>。</li>
<li><strong>它掛了、桌面才真的黑</strong>：反過來說，只要 kernel 還活著、compositor 以外的東西壞了，<a href="/blog/linux/dotfile/knowledge-cards/tty/" data-link-title="TTY" data-link-desc="恢復操作提到切 TTY 但不知道 TTY 是什麼時讀 — Linux 核心直接提供的純文字終端機介面">TTY</a> 仍能登入操作。判斷「畫面黑」是 compositor 掛了、還是沒 getty、還是顯示輸出沒接，是桌面故障排除的第一個分岔。</li>
</ul>
<p>相關概念：<a href="/blog/linux/dotfile/knowledge-cards/tty/" data-link-title="TTY" data-link-desc="恢復操作提到切 TTY 但不知道 TTY 是什麼時讀 — Linux 核心直接提供的純文字終端機介面">TTY</a>（不依賴 compositor 的救生通道）、<a href="/blog/linux/dotfile/knowledge-cards/session-lock/" data-link-title="Wayland Session Lock（鎖屏安全狀態）" data-link-desc="hyprlock / swaylock 畫面卡住、pkill 後進不了桌面、或要在 VM / 自動化環境測試鎖屏時回來讀">Session Lock</a>（compositor 持有的鎖定狀態）、<a href="/blog/linux/dotfile/knowledge-cards/rice/" data-link-title="Rice（桌面視覺客製化）" data-link-desc="Linux 桌面文章裡看到 rice / ricing / ricer 不確定意思時回來讀">Rice</a>（在 compositor 上做視覺客製）。</p>
]]></content:encoded></item><item><title>Wayland Session Lock（鎖屏安全狀態）</title><link>https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/session-lock/</link><pubDate>Wed, 01 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/session-lock/</guid><description>&lt;h2 id="鎖屏是-compositor-持有的安全狀態">鎖屏是 compositor 持有的安全狀態&lt;/h2>
&lt;p>Wayland 下的 compositor（如 Hyprland、Sway）同時管理視窗排列與畫面輸出。鎖屏工具（Hyprlock、Swaylock）一旦啟動，桌面的「鎖定」狀態就由 compositor 透過 ext-session-lock-v1（Wayland 生態系的跨 compositor 鎖屏協議）持有。解鎖的正常動作是鎖屏 client 通過認證後呼叫 &lt;code>unlock_and_destroy&lt;/code>（協議定義的 request），compositor 收到這個信號才釋放鎖定。&lt;/p>
&lt;p>這個責任邊界在自動化測試、VM 演練、遠端操作時最容易出事，因為這些情境常用「殺 process」當「關掉一個東西」的通用手段。殺掉鎖屏 client 跳過了認證，compositor 不會釋放鎖——畫面會卡在失效保護狀態而非回到桌面。&lt;/p>
&lt;h2 id="logind-提示與-compositor-鎖的值可以不一致">logind 提示與 compositor 鎖的值可以不一致&lt;/h2>
&lt;p>鎖屏狀態牽涉兩個獨立的層，觸發方向和持有者不同：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>層&lt;/th>
 &lt;th>持有者&lt;/th>
 &lt;th>查看方式&lt;/th>
 &lt;th>語意&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>logind 會話鎖&lt;/td>
 &lt;td>systemd-logind&lt;/td>
 &lt;td>&lt;code>loginctl show-session &amp;lt;id&amp;gt; -p LockedHint&lt;/code>&lt;/td>
 &lt;td>會話的鎖定提示，給登入管理器 / 螢幕保護程式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>compositor 鎖&lt;/td>
 &lt;td>Wayland compositor&lt;/td>
 &lt;td>畫面是否進得去、鎖屏 surface 是否在最上層&lt;/td>
 &lt;td>實際擋住畫面的那層&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;code>loginctl lock-session&lt;/code> 走 logind 層觸發鎖屏，鎖屏 client 收到信號後啟動、再向 compositor 取得 session lock。觸發方向是 logind → client → compositor；持有與強制執行方向是 compositor → 畫面。兩者方向相反，正好印證兩層是獨立的。&lt;/p>
&lt;p>實測會遇到 &lt;code>LockedHint=no&lt;/code>（logind 層說沒鎖）但畫面仍進不去——因為擋住畫面的是 compositor 的 ext-session-lock，跟 logind 提示是兩回事。判斷畫面進不進得去，看 compositor 層，不看 logind 層。&lt;/p>
&lt;h2 id="鎖屏-client-非正常結束時的失效保護">鎖屏 client 非正常結束時的失效保護&lt;/h2>
&lt;p>鎖屏 client 在持有鎖的狀態下死掉（被 &lt;code>kill&lt;/code>、crash），compositor 沒有收到認證通過的信號，只能維持鎖定並顯示失效保護畫面。Hyprland 的失效保護畫面會直接給恢復指令：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">hyprctl --instance 0 &amp;#39;keyword misc:allow_session_lock_restore 1&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">hyprctl --instance 0 &amp;#39;dispatch exec hyprlock&amp;#39;&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>allow_session_lock_restore&lt;/code> 允許新的鎖屏 client 接管既有的鎖（否則新 client 會因「已經鎖了」被拒）。接管後是乾淨的鎖屏 prompt，用密碼正常解鎖。&lt;/p>
&lt;p>備好 restore 路徑時，殺掉無回應的鎖屏 client 是合理操作——問題不在「殺」、在「以為殺完就回桌面」。restore 的前提是有另一個可操作的 session：另一個 TTY 或 SSH 連線。ext-session-lock 的安全語意允許 compositor 攔截 VT 切換快捷鍵（&lt;code>Ctrl+Alt+Fn&lt;/code>），遇到 TTY 切不過去的情況，SSH 是替代救援通道（事先配好 SSH server，見&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作&lt;/a>的 GPU hang 段）。&lt;/p>
&lt;h2 id="判讀與操作">判讀與操作&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>判讀鎖定狀態&lt;/strong>：&lt;code>loginctl show-session $(loginctl show-user $USER -p Display --value) -p LockedHint&lt;/code> 查 logind 層；compositor 層看畫面能否操作。兩層不一致時以 compositor 層為準。&lt;/li>
&lt;li>&lt;strong>正常解鎖&lt;/strong>：通過鎖屏 client 的認證（密碼 / 指紋），client 呼叫 &lt;code>unlock_and_destroy&lt;/code>，compositor 釋放鎖。&lt;/li>
&lt;li>&lt;strong>失效保護恢復&lt;/strong>：從另一個 TTY 或 SSH 執行 &lt;code>hyprctl --instance 0 'keyword misc:allow_session_lock_restore 1'&lt;/code> + &lt;code>hyprctl --instance 0 'dispatch exec hyprlock'&lt;/code>，重新拉起鎖屏 prompt 後認證解鎖。&lt;/li>
&lt;li>&lt;strong>自動化流程的代價&lt;/strong>：啟動鎖屏後，畫面會留在鎖定狀態直到有人通過認證。自動化測試若會觸發鎖屏，要把「需人工解鎖」算進代價。&lt;/li>
&lt;li>&lt;strong>診斷路由&lt;/strong>：「畫面卡住 / 螢幕鎖了沒」當成一般 Linux 狀態判讀問題（跟判程式活著、判服務歸屬同類）時，見&lt;a href="https://tarrragon.github.io/blog/linux/debug/process-service-state-diagnosis/" data-link-title="程序、服務與狀態怎麼判" data-link-desc="要判斷一個程式活著沒、某個系統服務現在由誰提供、桌面 session 有沒有被鎖、或終端機多工器的 session 還在不在時，用對的權威來源而不是靠畫面或猜的名字">程序、服務與狀態怎麼判&lt;/a>——它把「判 session 有沒有被鎖」放進「讀權威狀態、別看畫面猜」的通用診斷紀律裡。&lt;/li>
&lt;li>&lt;strong>延伸閱讀&lt;/strong>：鎖屏的視覺配置（背景、輸入框、時鐘 label）見&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/06-rice-design/color-system-theming/" data-link-title="配色系統、鎖屏與 GTK 主題" data-link-desc="桌面配色散亂看起來雜、或要換主題不知道該改哪些檔案時回來讀">配色系統、鎖屏與 GTK 主題&lt;/a>的 Hyprlock 段；桌面故障恢復流程見&lt;a href="https://tarrragon.github.io/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作&lt;/a>。持鎖的那個 compositor 到底是什麼、還握著哪些系統狀態，見 &lt;a href="https://tarrragon.github.io/blog/linux/dotfile/knowledge-cards/compositor/" data-link-title="Compositor（合成器）" data-link-desc="教材反覆出現 compositor / 合成器、想確認它到底負責什麼、跟 window manager 和桌面環境差在哪時讀 — Wayland 下把畫面合成與視窗管理合一的核心程式">Compositor 術語卡&lt;/a>。&lt;/li>
&lt;/ul>
&lt;h2 id="邊界條件">邊界條件&lt;/h2>
&lt;p>正常認證解鎖（走 &lt;code>unlock_and_destroy&lt;/code>）後鎖屏 client 結束，compositor 已回到非鎖定狀態，不觸發失效保護。失效保護只在「持鎖中非正常結束」時出現。&lt;/p></description><content:encoded><![CDATA[<h2 id="鎖屏是-compositor-持有的安全狀態">鎖屏是 compositor 持有的安全狀態</h2>
<p>Wayland 下的 compositor（如 Hyprland、Sway）同時管理視窗排列與畫面輸出。鎖屏工具（Hyprlock、Swaylock）一旦啟動，桌面的「鎖定」狀態就由 compositor 透過 ext-session-lock-v1（Wayland 生態系的跨 compositor 鎖屏協議）持有。解鎖的正常動作是鎖屏 client 通過認證後呼叫 <code>unlock_and_destroy</code>（協議定義的 request），compositor 收到這個信號才釋放鎖定。</p>
<p>這個責任邊界在自動化測試、VM 演練、遠端操作時最容易出事，因為這些情境常用「殺 process」當「關掉一個東西」的通用手段。殺掉鎖屏 client 跳過了認證，compositor 不會釋放鎖——畫面會卡在失效保護狀態而非回到桌面。</p>
<h2 id="logind-提示與-compositor-鎖的值可以不一致">logind 提示與 compositor 鎖的值可以不一致</h2>
<p>鎖屏狀態牽涉兩個獨立的層，觸發方向和持有者不同：</p>
<table>
  <thead>
      <tr>
          <th>層</th>
          <th>持有者</th>
          <th>查看方式</th>
          <th>語意</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>logind 會話鎖</td>
          <td>systemd-logind</td>
          <td><code>loginctl show-session &lt;id&gt; -p LockedHint</code></td>
          <td>會話的鎖定提示，給登入管理器 / 螢幕保護程式</td>
      </tr>
      <tr>
          <td>compositor 鎖</td>
          <td>Wayland compositor</td>
          <td>畫面是否進得去、鎖屏 surface 是否在最上層</td>
          <td>實際擋住畫面的那層</td>
      </tr>
  </tbody>
</table>
<p><code>loginctl lock-session</code> 走 logind 層觸發鎖屏，鎖屏 client 收到信號後啟動、再向 compositor 取得 session lock。觸發方向是 logind → client → compositor；持有與強制執行方向是 compositor → 畫面。兩者方向相反，正好印證兩層是獨立的。</p>
<p>實測會遇到 <code>LockedHint=no</code>（logind 層說沒鎖）但畫面仍進不去——因為擋住畫面的是 compositor 的 ext-session-lock，跟 logind 提示是兩回事。判斷畫面進不進得去，看 compositor 層，不看 logind 層。</p>
<h2 id="鎖屏-client-非正常結束時的失效保護">鎖屏 client 非正常結束時的失效保護</h2>
<p>鎖屏 client 在持有鎖的狀態下死掉（被 <code>kill</code>、crash），compositor 沒有收到認證通過的信號，只能維持鎖定並顯示失效保護畫面。Hyprland 的失效保護畫面會直接給恢復指令：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">hyprctl --instance 0 &#39;keyword misc:allow_session_lock_restore 1&#39;
</span></span><span class="line"><span class="ln">2</span><span class="cl">hyprctl --instance 0 &#39;dispatch exec hyprlock&#39;</span></span></code></pre></div><p><code>allow_session_lock_restore</code> 允許新的鎖屏 client 接管既有的鎖（否則新 client 會因「已經鎖了」被拒）。接管後是乾淨的鎖屏 prompt，用密碼正常解鎖。</p>
<p>備好 restore 路徑時，殺掉無回應的鎖屏 client 是合理操作——問題不在「殺」、在「以為殺完就回桌面」。restore 的前提是有另一個可操作的 session：另一個 TTY 或 SSH 連線。ext-session-lock 的安全語意允許 compositor 攔截 VT 切換快捷鍵（<code>Ctrl+Alt+Fn</code>），遇到 TTY 切不過去的情況，SSH 是替代救援通道（事先配好 SSH server，見<a href="/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作</a>的 GPU hang 段）。</p>
<h2 id="判讀與操作">判讀與操作</h2>
<ul>
<li><strong>判讀鎖定狀態</strong>：<code>loginctl show-session $(loginctl show-user $USER -p Display --value) -p LockedHint</code> 查 logind 層；compositor 層看畫面能否操作。兩層不一致時以 compositor 層為準。</li>
<li><strong>正常解鎖</strong>：通過鎖屏 client 的認證（密碼 / 指紋），client 呼叫 <code>unlock_and_destroy</code>，compositor 釋放鎖。</li>
<li><strong>失效保護恢復</strong>：從另一個 TTY 或 SSH 執行 <code>hyprctl --instance 0 'keyword misc:allow_session_lock_restore 1'</code> + <code>hyprctl --instance 0 'dispatch exec hyprlock'</code>，重新拉起鎖屏 prompt 後認證解鎖。</li>
<li><strong>自動化流程的代價</strong>：啟動鎖屏後，畫面會留在鎖定狀態直到有人通過認證。自動化測試若會觸發鎖屏，要把「需人工解鎖」算進代價。</li>
<li><strong>診斷路由</strong>：「畫面卡住 / 螢幕鎖了沒」當成一般 Linux 狀態判讀問題（跟判程式活著、判服務歸屬同類）時，見<a href="/blog/linux/debug/process-service-state-diagnosis/" data-link-title="程序、服務與狀態怎麼判" data-link-desc="要判斷一個程式活著沒、某個系統服務現在由誰提供、桌面 session 有沒有被鎖、或終端機多工器的 session 還在不在時，用對的權威來源而不是靠畫面或猜的名字">程序、服務與狀態怎麼判</a>——它把「判 session 有沒有被鎖」放進「讀權威狀態、別看畫面猜」的通用診斷紀律裡。</li>
<li><strong>延伸閱讀</strong>：鎖屏的視覺配置（背景、輸入框、時鐘 label）見<a href="/blog/linux/dotfile/06-rice-design/color-system-theming/" data-link-title="配色系統、鎖屏與 GTK 主題" data-link-desc="桌面配色散亂看起來雜、或要換主題不知道該改哪些檔案時回來讀">配色系統、鎖屏與 GTK 主題</a>的 Hyprlock 段；桌面故障恢復流程見<a href="/blog/linux/dotfile/07-desktop-maintenance/common-failures-recovery/" data-link-title="常見故障場景與恢復操作" data-link-desc="Hyprland 黑屏、waybar 消失、畫面凍結、記憶體爆掉或 config 寫錯導致進不了桌面時，按症狀查恢復操作">常見故障場景與恢復操作</a>。持鎖的那個 compositor 到底是什麼、還握著哪些系統狀態，見 <a href="/blog/linux/dotfile/knowledge-cards/compositor/" data-link-title="Compositor（合成器）" data-link-desc="教材反覆出現 compositor / 合成器、想確認它到底負責什麼、跟 window manager 和桌面環境差在哪時讀 — Wayland 下把畫面合成與視窗管理合一的核心程式">Compositor 術語卡</a>。</li>
</ul>
<h2 id="邊界條件">邊界條件</h2>
<p>正常認證解鎖（走 <code>unlock_and_destroy</code>）後鎖屏 client 結束，compositor 已回到非鎖定狀態，不觸發失效保護。失效保護只在「持鎖中非正常結束」時出現。</p>
<p>Sway/swaylock 在 client 死掉時沒有畫面上的恢復提示（不像 Hyprland 會印指令），得預先知道走 TTY 或 SSH 執行 restore。「鎖是 compositor 持有、解鎖要認證」是 ext-session-lock 協議層的共通約束；失效保護的具體呈現方式因 compositor 而異。</p>
]]></content:encoded></item></channel></rss>