TL;DR
- display-switch という OSS を使うと USB 切替器と連動してモニタの入力切り替えができる
- display-switch は DDC/CI プロトコルでモニタを制御するが、43UD79-B は使えない
- 代わりにシリアルケーブル接続で専用コマンドを用いれば制御が可能なので、display-switch の
on_usb_connect_executeで専用のコマンドを実行するようにすれば解決 - コマンドはgo で書いて、win,mac で利用するようにした
自分はメインモニタに 43UD79-B を使っていて、会社用の Mac と私物の Windows を接続しています
んで、これの入力切り替えを毎日やっていたわけなんですが、リモコンで操作しなきゃいけないので面倒だし机の上にリモコン置きっぱになるしでまあ不便だったんですよね
もう何年もずっと面倒だと思ったままほったらかしてたんですが、最近 @watiko に以下の OSS を教えてもらったことからことは始まりました
https://github.com/haimgel/display-switch/
このアプリは、USB デバイスの接続を検知してモニタの入力切り替えができる、というもので、USB 切替器を使えば 1 ボタンでマウスやらキーボードやらの切り替えとともにディスプレイの入力切り替えもできちゃう、というわけです
ところがどうもうちのモニタでは上手く動作しない
セットアップしてみるも、どうにも動かない
display-switch はログが親切だったので、そもそもモニタ側が変っぽいのは割とすぐわかりました(モニタの識別子の hex 値が規定の HDMI や DisplayPort のものと違い、 0x0 という値が返ってきていた)
んで調べてみると、Linux 向けの ddc/ci ツールである ddcutil の issue LG 29UM69G fails switching input にたどり着きました
まあなんか LG のモニタでトラブってる人がわんさかいるわけです
その議論を見るに、ddc を raw に操作できる方法があればデバッグできそうとなり、それを探すことにしました
https://www.ddcutil.com/mac_programs/
↑ に Mac 向けのツールなどが紹介されてたりもしたんですが、最終的には m1ddc を利用して、なんとかデバッグできるようになりました
そしてデバッグした結果、結局 43UD79-B は動かないな、という結論になって悲しみを背負ったのであった・・・
他に色々調べたんですが、ddcutil の以下のページにある 43UN700-B という同系列っぽいモニタも結果が NG だったので、DDC/CI での制御はダメそうかなあという結論に。
https://github.com/rockowitz/ddcutil/wiki/Switching-input-source-on-LG-monitors
光明
https://github.com/rockowitz/ddcutil/issues/100#issuecomment-1435477786
このコメントで、マニュアル見てどうこうしている、というのを見かけて、 43UD79-B でもなんか情報あるんじゃないかと見てみたら
EXTERNAL CONTROL DEVICE SETUP
とかいうセクションがあるじゃないですか
いけるやん!
・・・ただしシリアルケーブルを使いましょうという
しかもディスプレイ側の端子は 3.5mm のイヤホンジャックで、定番の D-sub9 ピンじゃない
Amazon でこんなのを買って、D-sub9 ピン →USB の変換を買って、両方ストレートケーブルだったのでリバース変換するケーブルを買って・・・など無駄な買い物もし・・・
あと自前のスクリプトが正しいのかも分からなかったので、シリアルケーブル経由で LG43UD79-B を操作するツールを書いてる人がいたっぽいので、これを持ってきて Windows で C#の環境作って VisualStudio でビルドして・・・とかやったりもしました
https://github.com/knarfalingus/LG43UD79
結果、なんとか疎通に成功しました
専用コマンドの実装
とりあえず knarfalingus/LG43UD79 が動いたので、まず powershell で適当に動かすようにしてみました
こんなん。
$c = New-Object System.IO.Ports.SerialPort("COM3")
$c.Open()
$c.Write("xb 01 C0`r")
$c.Close()
CR の送り方がわかってなくて \r とか書いて動いてなかったけど AI 様に聞いたら `r って書くんやでって言われました
AI 最高!
んでまあ動いたは動いたんですが、スリープ時のケアとかそういうの考え始めたらさすがにもうちょい恒久な言語で書きたいってなって Go で書きました
https://github.com/sisisin/op-lg43ud79
https://github.com/bugst/go-serial を使えば、シリアルポートデバイスの通信がクロスプラットフォームに出来るので、これを使って実装しています
自宅用のバイナリを吐くような作りになってはいますが、43UD79-B との通信部分はライブラリとしても使えるような気がします(試してないが)
色々試行錯誤したんですが、最終的には ↓ のような処理にまとまりました
- シリアルデバイスの一覧から VID,PID を元にして対象デバイスを探し、接続
- USB 切換器を使っていて、切替直後にデバイスが認識しない場合があるので、3 回ほど sleep & retry を入れる
- 接続後に、まず Power On コマンドを送信
- モニタがスリープしてるケースのケア
- 最後に入力切替コマンドを送信
このコマンドを display-switch の on_usb_connect_execute で実行するようにすれば、USB 切替器の切り替えと連動してモニタの入力切り替えができるようになります :tada:
色々試行錯誤の思い出
- コマンドを送信しただけじゃコマンドが実施されず、応答を読みこんで初めて反応した
- そういうものなの?
- go-serial の port が
io.Readerinterface 実装されてるので、io.ReadAllしたれ!ってやると止まるio.ReadAllは EOF まで読もうとするが、モニタの返す末尾が EOF ではなくCRなので使えない(なるほどね)
CRで終端処理をしなきゃいけないと気付いてなくて、Read がブロッキングされて CTRL+C でも止められなくて悲哀、みたいなことを何度もやった- 素人みを感じる
- signal.NotifyContext と ctx.Done は便利
- 最初に mac で試していた bash 版スクリプト
DEVICE="/dev/cu.usbserial-FTEM5CHZ"
stty -f "$DEVICE" 9600 cs8 -cstopb -parenb -ixon -ixoff -crtscts raw -echo
COMMAND="$1"
SET_ID="$2"
DATA="$3"
# コマンド形式: [Command1][Command2][ ][Set ID][ ][Data][Cr]
echo -ne "${COMMAND} ${SET_ID} ${DATA}\r" > "$DEVICE"
timeout 0.1s cat < "$DEVICE" 2>/dev/null
- そもそもデバイス探してそこに向かって通信投げるみたいなの初めて書いたので新鮮だった
go-serialがクロスプラットフォームに動くおかげでマジでほとんど何も考えなくても win,mac で動くように作れて最高。やはりこういうときに Go 言語はかなりいい- Rust に入門した
- モニタの挙動確認がしたくて、display_switch のログ出しを変更する PR を出した
- if が式な言語久しぶりすぎる
謝辞
display_switch 教えてくれたり、調査に色々付き合ってくれた @watiko に感謝 :pray:
おわりに
ということで無事ちゃんと動くようになりました。最高!