前言

前段時間需要離開學校使用電腦工作,在學校的顯示器尺寸實在太大,因此購買了一個可攜式顯示器。

在回到學校後,這個可攜式顯示器就作為了輔助顯示器使用。然而,儘管 Hyprland 支援多顯示器,其工作區 (Workspace) 切換的設定卻並不符合我的使用習慣。工作區可以在屏幕之間移動,但這需要配置額外的組合鍵。此外,當切換到另一個顯示器的工作區上時,焦點和光標也會移動到另一個工作區/顯示器上,這會讓我一時找不到焦點和光標在哪。

如果是我,我應該會為每個顯示器分配 10 個工作區。當焦點在主顯示器上時,就切換到主顯示器上的工作區。當焦點在副顯示器上時,就切換到副顯示器上的工作區。這樣的邏輯不太能單純使用 Hyprland 的設定檔實現,因此需要藉助 sh 腳本的力量來實現我們的邏輯。

思路

Hyprland 的每個工作區有一個工作區 ID,每個顯示器也有一個顯示器 ID。那麼我們可以為 ID 為 0 的顯示器分配 ID 為 1~10 的工作區,為 ID 為 1 的顯示器分配 ID 為 11~20 的工作區,以此類推。在按下組合鍵時,檢測焦點在哪個顯示器上,然後切換到對應的工作區。

Hyprland Dispatcher

Hyprland 有一系列的內建操作,例如切換工作區、移動工作區、移動窗口、執行指令等。這些操作可以透過 Hyprland 設定檔中配置組合鍵執行:

bind = $mainMod, 1, workspace, 1
# 當按下 mod + 1 時,切換到 1 號工作區
bind = $mainMod SHIFT 1, movetoworkspace, 1
# 當按下 mod + Shift + 1 時,將當前視窗移動到 1 號工作區,並切換到 1 號工作區
bind = $mainMod SHIFT, comma, movecurrentworkspacetomonitor, l
# 當按下 mod + Shift + "," 時,將當前工作區移動到左邊的顯示器上
bind = $mainMod T, exec, alacritty
# 當按下 mod + T 時,執行 alacritty 指令,啟動 Alacritty 終端模擬器

也可以透過 hyprctl dispatch 指令執行:

hyprctl dispatch workspace 1
hyprctl dispatch movetoworkspace 1
hyprctl dispatch movecurrentworkspacetomonitor l
hyprctl dispatch exec alacritty

那麼,可以讓切換工作區的組合鍵按下後執行一個 sh 腳本,在這個腳本中獲取焦點所在的顯示器,並將工作區切換到指定的顯示器上。

sh 腳本

檢測焦點所在的顯示器

hyprctl 提供了子指令 hyprctl activeworkspace,可以用於獲取當前活躍工作區的狀態。在終端中執行這個指令可以得到類似如下的輸出:

workspace ID 2 (2) on monitor DP-1:
	monitorID: 0
	windows: 4
	hasfullscreen: 0
	lastwindow: 0x627422603b10
	lastwindowtitle: hyprctl activeworksp ~

不難從第 2 行看出,當前活躍的工作區所屬的顯示器 ID 為 0。透過 grepawk 即可將數字部分提取出來:

hyprctl activeworkspace | grep "monitorID" | awk '{print $2}'

其中,grep 將會從 hyprctl activeworkspace 的輸出中提取出包含 monitorID 的這一行。而 awk 將會提取出用空格分割的第 2 個字串,也就是 0

在 sh 腳本中,將將輸出保存為一個變量:

monitor_id=$(hyprctl activeworkspace | grep "monitorID" | awk '{print $2}')

參數設計

如你所見,這個腳本也需要一些輸入參數,例如要切換到哪個工作區,以及是否需要將當前活動的視窗也移動到那個工作區。在 sh 腳本中,可以透過 $ 加上參數編號來獲取執行腳本時的參數。

在這裡,考慮將腳本的第 1 個參數作為操作類型,將第 2 個參數作為我們為工作區。

operation=$1
workspace=$2

operationswitch 時,則切換到目標工作區;當為 move 時,則將當前視窗移動到目標工作區,然後切換到目標工作區。

操作工作區

首先當然是計算目標工作區 ID。知道了顯示器 ID 和工作區編號參數之後,在 sh 腳本中計算目標工作區 ID:

$workspace_id=$(($monitor_id * 10 + $workspace))

然後根據操作類型,透過 hyprctl 來執行對應的操作:

if [[ $operation == "switch" ]]; then
	hyprctl dispatch moveworkspacetomonitor $workspace_id $monitor_id;
	hyprctl dispatch workspace $workspace_id;
fi
if [[ $operation == "move" ]]; then
	hyprctl dispatch moveworkspacetomonitor $workspace_id $monitor_id;
	hyprctl dispatch movetoworkspace $workspace_id;
fi

您可能會注意到,在執行切換或移動工作區的操作前,還會首先將目標工作區移動到焦點所在的顯示器上。這是因為目標工作區可能並不在焦點所在的顯示器上,因此需要首先將其移動到焦點所在的顯示器上。

最終的腳本

#!/bin/bash

operation=$1
workspace=$2

monitor_id=$(hyprctl activeworkspace | grep "monitorID" | awk '{print $2}')
workspace_id=$(($monitor_id * 10 + $workspace))
echo "Final Operation: $operation to $workspace_id"

if [[ $operation == "switch" ]]; then
	hyprctl dispatch moveworkspacetomonitor $workspace_id $monitor_id;
	hyprctl dispatch workspace $workspace_id;
fi
if [[ $operation == "move" ]]; then
	hyprctl dispatch moveworkspacetomonitor $workspace_id $monitor_id;
	hyprctl dispatch movetoworkspace $workspace_id;
fi

在接下來的操作中,我將這個腳本保存為 ~/.config/hypr/switch_workspace.sh 這一檔案。

Hyprland 配置

原本預設的配置如下:

# Switch workspaces with mainMod + [0-9]
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
# ...
bind = $mainMod, 0, workspace, 10

# Move active window to a workspace with mainMod + SHIFT + [0-9]
bind = $mainMod, 1, movetoworkspace, 1
bind = $mainMod, 2, movetoworkspace, 2
# ...
bind = $mainMod, 0, movetoworkspace, 10

現在將其修改為在按下組合鍵時,執行腳本:

$switch_script = ~/.config/hypr/switch_workspace.sh

# Switch workspaces with mainMod + [0-9]
bind = $mainMod, 1, exec, $switch_script switch 1
bind = $mainMod, 2, exec, $switch_script switch 2
# ...
bind = $mainMod, 0, exec, $switch_script switch 10

# Move active window to a workspace with mainMod + SHIFT + [0-9]
bind = $mainMod, 1, exec, $switch_script move 1
bind = $mainMod, 2, exec, $switch_script move 2
# ...
bind = $mainMod, 0, exec, $switch_script move 10

在 Hyprland 中設定好組合鍵之後,所有工作就完成了。

结语

儘管一個程式可能並不支援複雜的操作,但透過 sh 腳本,可以將不同的程式組合起來,以達成想要做到的複雜的操作。對於 Hyprland 的多顯示器設定來講,這就是一個利用 sh 腳本來將桌面環境改造得更適合個人習慣的例子。

此外,本文所述的功能亦可用過 split-monitor-workspaces 插件實現,該插件添加了多個 Dispatcher,將工作區分割到多個顯示器上,並在每個顯示器上提供獨立的編號。個人傾向於使用 sh 腳本實現,編寫 Hyprland 插件對我來講,可能會相對麻煩一些。

希望這篇文章可以為您提供改造桌面環境的思路和幫助。


参考资料