コマンド×X Window System×キーボード/マウス×ウィンドウ
Live With Linux::technique
- TOP
- Articles
- Live With Linux
- コマンド×X Window System×キーボード/マウス×ウィンドウ
序
この記事はコマンドでウィンドウの情報を得たり操作したり、 コマンドからキーボードやマウスの操作をしたり、 逆にキーボードやマウスからコマンドを発生させたりするものである。
今のGoogleだと検索したときに探しているものとは逆のものに吸われて、どうキーワードを変えてもまともに検索できないので、ここに書くことにした。
コマンドでキーボードやマウスを操作する
「オートメーション」とか「RPA」とか場合によっては「マクロ」とか呼ばれているものに有効。
xdotool
(えっくすどぅーつぅーる)
というものがあり、こちらで操作することができる。
キーボード
キーボード操作を発生させるには
xdotool key a
のようにする。 Shiftを伴う場合は
xdotool key shift+a
Ctrlを伴う場合は
xdotool key ctrl+a
CtrlとAltとShiftの同時押しなら
xdotool ctrl+alt+shift+a
のようにする。
非アルファベットキーは
Return
, Space
, Tab
,
BackSpace
, Up
, Down
,
Left
, Right
, Home
F1
, Page_Up
, Print
など。
Superキー(Windowsキー)はSuper_L
,
Super_R
がある。
テキスト入力はtype
というアクションがあり、
xdotool type Hello
とかできる。
マウス
マウス関連は指定座標に移動する
xdotool mousemove 200 200
や、相対的に移動させる
xdotool mousemove_relative -- -50 50
負の値を与えるときには--
が必要なことに注意。
クリックするときはclick
。マウスボタンの番号を与えるのだが、Windows流に「左クリック、右クリック」とか覚えている人は要注意。
一般的には左クリックが1, 右クリックが2,
ホイールクリックが3になっている。でも、3ボタンマウスを使っていると左から順に1,
2, 3だ(そんな人はほぼいないだろうけど)。
xdotool click 1
ダブルクリックはこう
xdotool click --repeat 2 1
分ける必要があるならmousedown
とmouseup
を使う。
ゲームとかで自動化しようと思うならば、windowsize
とwindomove
が有効。
WindowIDを予め取得する必要があるが
xdotool windowmove 11111111 0 0
xdotool windowsize 11111111 1000 600
とかすればウィンドウ座標が一定になるから、座標指定が楽になる。
同じような機能を持つものとしてxautomation(コマンドはxte
)もある。
ただし、機能はxdotoolのほうが多いようだ。
デスクトップから情報を取得する
WindowIDを得る
ウィンドウ操作に必要なウィンドウIDを得る方法で、一番実用的なのはクリックしたウィンドウのWindowsIDを得るselectwindow
。
xdotool selectwindow
このIDはxwininfo
の-id
オプションでも使える。
ウィンドウの座標と大きさ
xwininfoを使う…のだけども注意が必要な部分もある。
-frame
をつけるとウィンドウマネージャのフレームを含むようになるのだが、これをすると正確な値が出ない。
-frame
をつけるのは諦めたほうが良い(slopを使えば取れる)。
また、-geometry
の値はおそらくほしい値ではない。
これは、Corenersの左上からRelativeのXとYを引いた値と、Cornersの右下の差分で出しているのだが、単純にAbsoluteの値とWidth/Heightで得る値が正しい。
つまり、
eval $(xwininfo -id 11111111 | sed 's/^ *//' | grep -e '^Absolute' -e '^Height' -e '^Width' | sed -e 's/: */=/' -e 's/^/_wininfo_/' | tr ' -' '__')
って感じ。
xdotoolにもgetwindowgeometry
というコマンドがあるのだが、こちらも正確な値が得られない。
後述するようにslopを使う方法もある。
slop
デスクトップ上で矩形選択した領域の情報を取得
slop
を使えば実現できる。
% slop
979x749+4397+784
slopはクリックするとそのウィンドウの情報が得られる。この場合、ウィンドウボーダーを含む正確なウィンドウが得られる。 わかりやすく使えるから、
slop -c 0.2,0.3,1,0.7 -l
とかやると結構便利。
slopよりもxrectselのほうが有名なようだけど、これはちょっと複雑でlolilolicon版、gvalkov版、digitoronikによるPython版と多彩。 また、gvalkovのほうではslopを勧めている。
マウスポインタの位置を得る
xdotool getmouselocation
クリックポイントを得たい場合に有効。
今マウスポインタが乗っているウィンドウのIDも得られる。
--shell
オプションを使うとeval
できる形になる。
簡易画像認識
オートメーションしようと思うと割と状態識別の機能がいる。 特に画像認識をしたいことは多いだろう。
本格的な画像認識は難しいが、ソシャゲ程度なら割となんとかなる。
まずは状態を定義した画面を取得する。PNGかビットマップにしておくこと。
ffmpeg -f x11grab -video_size 1000x600 -i $DISPLAY+10,27 -frames:v 1 stateA.png
可変部分(ステータスの値や時間など)を塗りつぶす。
mogrify -fill black -draw "rectangle 20,50,150,150" stateA.png
これで準備完了。 判定する必要があるところでスクショを撮り、同じように塗りつぶす。
ffmpeg -y -f x11grab -video_size 1000x600 -i $DISPLAY+10,27 -frames:v 1 state-current.png
mogrify -fill black -draw "rectangle 20,50,150,150" state-current.png
ImageMagickを使って比較対象との状態を比べる。 SSIMによる比較は近似であるほど1に近づく。全く同じである場合はinfになる。 fuzzで少し削ってあげたほうがいいかもしれない。
diff=$(compare -fuzz 30% stateA.png state-current.png NULL:)
if [[ $diff == "inf" ]] || (( diff > 0.85 ))
then
# 想定する画面が来ている場合の処理
# ...
fi
日本語はまず無理だけど、英語(特に数値)に関してはOCRで認識できる可能性がある。
対象の画像を厳密に取得し、tesseractで抜き出す。
ffmpeg -y -f x11grab -video_size 224x21 -i $DISPLAY+460,120 -frames:v 1 state-current.png
value=$(tesseract state-current.png -)
日本語はtesseractよりはocrmypdfのほうがマシではあるけど、期待はしないほうがいい。
convert state-current.png state-current.pdf
ocrmypdf -l jpn state-current.pdf ocr.pdf
value=$(pdftotext ocr.pdf -)
rm ocr.pdf
ショートカットキーでコマンドを実行する
ウィンドウマネージャやデスクトップ環境にその機能があったりする。
それで足りない場合や、それが機能しない場合はxbindkeysを使うと良い。
簡易で実践的な例
Enterキーによるメッセージ送りができない(マウスクリックで送る)ソシャゲでEnter送りを可能にする。ついでに、PgUp/PgDnをホイールに当てている。
Enterキーの入力はxev
を使って拾っている。
クリックを要求するxdotool selectwindow
の直後にxdotool getmouselocation
を入れることで、クリックした座標を取得する、というテクニックを使っている。
このコードだと空行が入るので、単純に*)
という条件を使うことはできない。
また、xevは別にデフォルトの挙動をキャンセルするわけではないので、これらのキーが何も効果を及ぼさないことを前提にしていることに注意してほしい。
#!/bin/zsh
winid=$(xdotool selectwindow)
eval $(xdotool getmouselocation --shell)
xev -id $winid | perl -00 -nl -e '$|=1;' -e '/XmbLookupString/ && /keycode ([^ ]+)/ && print $1;' | while read
do
xdotool windowfocus $winid
xdotool mousemove $X $Y
case $REPLY in
36)
xdotool click 1
;;
112)
xdotool click 4
;;
117)
xdotool click 5
;;
esac
done
まぁ、あんまりマウスホイールで履歴が見られるソシャゲもないので、それはそれで別に座標を設定してあげるとかすることになるだろうけども。
なお、わかりやすくいじりやすい題材だからソシャゲを持ってきたのであって、この記事を書くきっかけになったのはソシャゲではない。
普段テキスト送りをマウスでやっている人なら気にならないかもしれないが、個人的にはキーボードでテキスト送りができるととても快適。ソシャゲのストーリーを読もうかという気持ちが2割くらい増える。