Ubuntu 24.04のホームリモート環境 (Moonlight+Sunshine) でホスト側のデバイスを一時的に無効にしたいと思ってスクリプトを書いてみました。なおホスト環境とホームリモート環境の切り替えはホスト側に接続されたマクロキーボード(MKESPN K806)で行っています(参考)。
現状ではディスプレイの復帰でWMの設定やウインドウの位置がおかしくなったりで、まだオススメできる段階ではないですが、方向は合ってるはずなので誰かの参考になれば幸いです。
ホームリモート環境への切り替え:
switch_to_sunshine.sh
#!/bin/sh
# PID を取得
PID=$(pgrep -x sunshine)
if [ -z "$PID" ]; then
echo "Sunshine is not running. Starting..."
/usr/bin/sunshine &>/dev/null &
fi
# プロセス状態を確認
STAT=$(ps -o stat= -p "$PID")
if echo "$STAT" | grep -q "[ZT]"; then
echo "Sunshine exists but is stopped or zombie (STAT=$STAT). Restarting..."
kill -9 "$PID" 2>/dev/null
/usr/bin/sunshine &>/dev/null &
fi
# ポート応答チェック
if ! ss -u -l | grep -q "$SUNSHINE_PORT"; then
echo "Sunshine not responding on port $SUNSHINE_PORT. Restarting..."
pkill -x sunshine
/usr/bin/sunshine &>/dev/null &
fi
if ! ss -u -l | grep -q "$SUNSHINE_PORT"; then
echo "Sunshine is not running because of unknown error."
exit 1
fi
echo "Sunshine is running and responding."
# ddcutil detect 出力から I2C bus と Product code を取得
mapfile -t displays < <(ddcutil detect --sleep-multiplier 0.1 | awk '
/Display [0-9]+/ {display_id=$2}
/I2C bus:/ {i2c=$3}
/Product code:/ {code=$3; print i2c ":" code}
')
# ディスプレイごとに消灯
for display in "${displays[@]}"; do
bus="${display%%:*}"
code="${display##*:}"
# Product code に応じて値を決定
case "$code" in
48322) # MSI MPG 274URF QD
# https://qiita.com/vipper36/items/9d400c6147f5b4af94e2
value=4
;;
19771) # ViewSonic VX2718 Series
value=5
;;
*)
echo "Unknown product code $code on bus $bus, skipping."
continue
;;
esac
echo "Setting value $value on bus $bus (Product code $code)"
DDCCONTROL_NO_DAEMON=1 ddccontrol -r 0xd6 -w "$value" "dev:$bus" &
done
# スクリーンセーバーのロックを無効にする
gsettings set org.gnome.desktop.screensaver lock-enabled false
# ホスト側のオーディオ出力をミュートにする
DEFAULT_SINK=$(LANG=C pactl info | grep "Default Sink" | awk '{print $3}')
pactl set-sink-mute "$DEFAULT_SINK" 1
# バグりやすいため無効化
if [ ]; then
# 無効化したくないデバイス名のリスト(改行区切り)
# 重要: "INSTANT USB GAMING MOUSE Keyboard" (MKESPN K806) の部分は復帰用デバイスを指定する
EXCLUDE_DEVICES=$(cat <<'END'
Virtual core pointer
Virtual core keyboard
Virtual core XTEST pointer
Virtual core XTEST keyboard
Mouse passthrough
Keyboard passthroughxin
Power Button
Sleep Button
INSTANT USB GAMING MOUSE Keyboard
END
)
# masterの番号を保存
xinput list > /tmp/xinput_list
# xinput listから "名前, ID" をまとめて取得してループ
xinput list | grep -oP '↳\s+.*id=\d+' | while read -r line; do
# 名前部分とID部分を分割
dev=$(printf "%s" "$line" | sed -E 's/↳\s*([^ ].*[^ ])\s+id=.*/\1/')
id=$(printf "%s" "$line" | sed -E 's/.*id=([0-9]+).*/\1/')
# 除外リストに含まれていなければ無効化解除
echo "$EXCLUDE_DEVICES" | grep -Fxq "$dev"
if [ $? -ne 0 ]; then
echo "Disabling $dev (ID: $id)"
# xinput disable "$id"
xinput float "$id"
fi
done
fi
# 無効化するメインマウスとメインキーボード
MAIN_MOUSE="Logitech G502 X"
MAIN_KEYBOARD="Corsair CORSAIR K70 MAX RGB Magnetic-Mechanical Gaming Keyboard"
line=`xinput list | grep -P "$MAIN_MOUSE\s+id=" | grep "slave pointer"`
dev=$(printf "%s" "$line" | sed -E 's/.*↳\s*([^ ].*[^ ])\s+id=.*/\1/')
id=$(printf "%s" "$line" | sed -E 's/.*id=([0-9]+).*/\1/')
echo "Disabling $dev (ID: $id)"
xinput float "$id"
line=`xinput list | grep -P "$MAIN_KEYBOARD\s+id=" | grep "slave keyboard"`
dev=$(printf "%s" "$line" | sed -E 's/.*↳\s*([^ ].*[^ ])\s+id=.*/\1/')
id=$(printf "%s" "$line" | sed -E 's/.*id=([0-9]+).*/\1/')
echo "Disabling $dev (ID: $id)"
xinput float "$id"
ホスト環境への切り替え:
switch_from_sunshine.sh
#!/bin/sh
# PID を取得
PID=$(pgrep -x sunshine)
# プロセスを終了
if [ -n "$PID" ]; then
kill -9 "$PID" 2>/dev/null
fi
# ddcutil detect 出力から I2C bus と Product code を取得
mapfile -t displays < <(ddcutil detect --sleep-multiplier 0.1 | awk '
/Display [0-9]+/ {display_id=$2}
/I2C bus:/ {i2c=$3}
/Product code:/ {code=$3; print i2c ":" code}
')
# ディスプレイごとに消灯より復帰
for display in "${displays[@]}"; do
bus="${display%%:*}"
code="${display##*:}"
# Product code に応じて値を決定
case "$code" in
48322) # MSI MPG 274URF QD
# https://qiita.com/vipper36/items/9d400c6147f5b4af94e2
value=2
;;
19771) # ViewSonic VX2718 Series
value=1
;;
*)
echo "Unknown product code $code on bus $bus, skipping."
continue
;;
esac
echo "Setting value $value on bus $bus (Product code $code)"
DDCCONTROL_NO_DAEMON=1 ddccontrol -r 0xd6 -w "$value" "dev:$bus" &
done
# スクリーンセーバーのロックを有効にする
gsettings set org.gnome.desktop.screensaver lock-enabled true
# ホスト側のオーディオ出力をミュートより復帰
DEFAULT_SINK=$(LANG=C pactl info | grep "Default Sink" | awk '{print $3}')
pactl set-sink-mute "$DEFAULT_SINK" 0
# バグりやすいため無効化
if [ ]; then
# 無効化したくないデバイス名のリスト(改行区切り)
# 重要: "INSTANT USB GAMING MOUSE Keyboard" (MKESPN K806) の部分は復帰用デバイスを指定する
EXCLUDE_DEVICES=$(cat <<'END'
Virtual core pointer
Virtual core keyboard
Virtual core XTEST pointer
Virtual core XTEST keyboard
Mouse passthrough
Keyboard passthrough
Power Button
Sleep Button
INSTANT USB GAMING MOUSE Keyboard
END
)
# xinput listから "名前, ID, Master ID" をまとめて取得してループ
cat /tmp/xinput_list | grep -oP '↳\s+.*id=\d+.*\(\d+\)\]' | while read -r line; do
# 名前部分とID部分を分割
dev=$(printf "%s" "$line" | sed -E 's/↳\s*([^ ].*[^ ])\s+id=.*/\1/')
id=$(printf "%s" "$line" | sed -E 's/.*id=([0-9]+).*/\1/')
master=$(printf "%s" "$line" | sed -E 's/.*\(([0-9]+)\)\].*/\1/')
# 除外リストに含まれていなければ無効化解除
echo "$EXCLUDE_DEVICES" | grep -Fxq "$dev"
if [ $? -ne 0 ]; then
echo "Enabling $dev (ID: $id)"
# xinput enable "$id"
xinput reattach "$id" "$master"
fi
done
fi
# 復帰するメインマウスとメインキーボード
MAIN_MOUSE="Logitech G502 X"
MAIN_KEYBOARD="Corsair CORSAIR K70 MAX RGB Magnetic-Mechanical Gaming Keyboard"
line=`xinput list | grep -P "$MAIN_MOUSE\s+id=" | grep "floating slave"`
dev=$(printf "%s" "$line" | sed -E 's/.*↳\s*([^ ].*[^ ])\s+id=.*/\1/')
id=$(printf "%s" "$line" | sed -E 's/.*id=([0-9]+).*/\1/')
master=`xinput list | grep "master pointer" | sed -E 's/.*id=([0-9]+)\s.*/\1/'`
echo "Enabling $dev (ID: $id)"
xinput reattach "$id" "$master"
line=`xinput list | grep -P "$MAIN_KEYBOARD\s+id=" | grep "floating slave"`
dev=$(printf "%s" "$line" | sed -E 's/.*↳\s*([^ ].*[^ ])\s+id=.*/\1/')
id=$(printf "%s" "$line" | sed -E 's/.*id=([0-9]+).*/\1/')
master=`xinput list | grep "master keyboard" | sed -E 's/.*id=([0-9]+)\s.*/\1/'`
echo "Enabling $dev (ID: $id)"
xinput reattach "$id" "$master"