あらゆる操作を実行可能マウスカーソルを動かす方法いろいろ

GUIなOS環境では、マウスを操作してカーソルを移動することで、操作対象を選択して、命令を実行する。通常マウスは人の手で操作するのだけど、もしコードで自由に制御できれば、面倒な一連のマウス操作を自動化できるのだ。

但し、人がマウスを操作する時は画面の状況を確認しながら操作できるけど、マウスカーソルを自動制御する時には、そうはいかない。今時のOSXでは、ウィンドウがしまわれていたり、アプリが隠れていたり、Spacesでスペースがいくつもあったりと、考慮しておくことが多くて大変なのだ。
一方、マウスを操作して実行する命令には、大抵ショートカットが用意されていたり、同等のコマンドがあったりする。AppleScriptでも操作可能かもしれない。可能な限りマウスカーソルの自動制御以外の方法で操作した方が、GUIの状況に左右されず、素早く、確実に、命令が完了するはずである。

そうは言っても、マウス以外の操作方法が見当たらないことも多々ある。あるいは、GUIスクリプティングがどうにも上手く動いてくれないとか、コードの書き方がよくわからないとか。そんな時、マウスカーソルを自動制御する方法を知っていれば、かなり楽して幸せになれる。マウスカーソルをコードで制御するのは、冗長で、環境にも思いきり依存し、そこでしか使えない限定的なツールになりがちだが、覚えておいて損はないと思われる。調べてみた。

AppleScriptの click atコマンド

  • 以下のように click atコマンドを使うことで、目標の座標(x=36, y=33)がクリックされる。
    • 座標(x=36, y=33)は、AppleScriptエディタのウィンドウのオレンジボタン。
    • 実行すると、Dockにウィンドウがしまわれる。(アニメーション付き)
  • 但し、目標の座標をクリックするイベントが発生するだけで、現在のマウスカーソルの位置は変化しない。
  • 目標の座標は、画面左上を原点(0, 0)とした絶対座標で指定する。
    • MacBook13インチの場合は、画面右下(1280, 800)までの範囲となる。
  • ちなみに、絶対座標は、スクリーンショットcommand-shift-4のショートカットで簡単に計測できる。


tell application "System Events"
tell process "AppleScript Editor"
click at {36, 33} end tell
end tell

Automatorで記録する

  • Automatorの「記録」ボタンを押すと、簡単にGUI操作がワークフローとして記録される。
  • 記録したワークフローは、「実行」ボタンを押すことで再生される。
  • 以下のワークフローは、アップルメニューのクリックを記録したもの。

  • 再生速度を×10にしておくと、ほとんど待ち時間なく、テキパキと実行してくれる。
  • マウス操作を記録したワークフローを実行すると、現在のマウスカーソルの位置がワークフローに従って動く。
  • ちなみに、記録されたイベントを、ワークフローエリアにドラッグ&ドロップすると、AppleScriptに変換される。
  • 未知のGUIスクリプティングの記述方法を知る便利な方法でもある。

cliclickコマンド

$ cliclick -h

 cliclick - Command Line Interface Click
 Version 1.3.1
 Carsten Bluem, 2010-04-13
 http://www.bluem.net/en/mac/cliclick/

 Usage: cliclick [-v] [-r] [-q] [-w n] x y [x2 y2] [x3 y3] [...]
   x and y are integer numbers which specify the screen coordinate(s)
   where the mouse click(s) should be emulated. (Upper left corner is 0 0)
   Instead of a number, you may pass "m" as x and / or value to
   use the current x and / or y position.

   If you need a doubleclick, prefix the x coordinate with "d".
   If you need a control click, use prefix "c".

 Options:
   -w  You can pass multiple coordinate pairs as argument. But if you do,
          it is often useful to have a small delay between events -- that is
          what the -w option is for: It will cause cliclick to wait for the
          specified number of milliseconds after each event.
   -q     Instead of clicking, print the current mouse pointer.
          location on the screen (format "x,y") and exit.
   -r     Restore initial mouse location after performing the clicks.
   -v     Makes cliclick more verbose.

 Examples:
   'cliclick 26 12' will click the apple menu
   ((x=26, y=12)の座標をクリックする。つまり、アップルメニューをクリックする。)

   'cliclick 50 60 c70 80' will click at 50/60, then Control-click at 70/80
   (cに続く座標はControl-click、つまり右クリックと同等になる。)

   'cliclick d50 60' will doubleclick at 50/60
   (dに続く座標はダブルクリックになる。)

   'cliclick dm m' will doubleclick the current mouse location
   ((m,m)は現在のマウスカーソルの座標、と読まれる。)

   'cliclick c500 m' will control-click at x position 500 and the mouse's current y position.
   (500,m)は、x=500、y=現在のy座標、と読まれる。)

   'cliclick -w 50 26 11 26 33' will open the "About this Mac" panel
   (-wに続く数値はミリ秒指定。連続クリックする時の間隔が指定される)

   'cliclick -r 26 12' will click the apple menu and, afterwards, restore the initial mouse location.
   (-rオプションは、cliclickコマンド実行後、実行前のマウスカーソル位置に戻す)

   'cliclick -q' will print the current mouse location.
   (-qオプションは、現在のマウス座標を返す)
$ cliclick -q
317,515
  • cliclickコマンドでは、クリックの連続技を簡潔なワンライナーで記述できるところが好き。

クリックせずにカーソルだけ移動する

  • 上記はすべてマウスクリックの操作だったが、クリックせずにマウスカーソルの移動のみは出来ないのだろうか?
MacRuby


do shell script "/usr/local/bin/macruby -e \"framework 'carbon'\" -e \"framework 'ApplicationServices'\" -e \"CGWarpMouseCursorPosition(CGPointMake(" & 26 & "," & 12 & "))\""

  • 上記AppleScriptは、マウスカーソルを座標(x=26, y=12)、つまりアップルメニュー アイコンの真ん中に移動するのだ。
  • 以下のようにmove_mouseハンドラとして登録しておけば、使いやすいかもしれない。


move_mouse(26, 12)

on move_mouse(x, y)
do shell script "/usr/local/bin/macruby -e \"framework 'carbon'\" -e \"framework 'ApplicationServices'\" -e \"CGWarpMouseCursorPosition(CGPointMake(" & x & "," & y & "))\""
end move_mouse


CocoaRuby版(あるいはRubyCocoaと呼ばれる)


do shell script "/usr/bin/ruby -e \"require 'osx/cocoa'\" -e \"OSX::CGWarpMouseCursorPosition(OSX::CGPointMake(" & 26 & "," & 12 & "))\""


move_mouse(26, 12)

on move_mouse(x, y)
do shell script "/usr/bin/ruby -e \"require 'osx/cocoa'\" -e \"OSX::CGWarpMouseCursorPosition(OSX::CGPointMake(" & x & "," & y & "))\""
end move_mouse


マウスボタンを押す・放す

  • 「クリックせずにカーソルだけ移動する」方法では、確かにマウスカーソルは移動するが、実際に手でマウスを握って動かすのと状況は異なる。
  • move_mouse()を使ってメニュー項目上に移動しても、ハイライト表示もされず、サブメニューも表示されないのだ。
  • おそらくCGWarpMouseCursorPositionは、マウスカーソルを特定の位置にセットするだけで、マウスを動かしたというイベントは発生させないのだ。
  • キー操作でメニュー選択をする方法もあるが、フルキーボードアクセスを有効にしても操作できないメニュー項目もあったりする。
    • メニューバー右側のアイコンメニューの中に、フルキーボードアクセスでは届かない項目が存在することが多い。
  • cliclickコマンドを利用しても良いのだが、クリックする一歩手前で項目をハイライトした状態で停止させておきたい需要もある。

こうなったら、マウスボタンのキーダウン・キーアップをシミュレートするしかない!


--左のマウスボタンをダウン(押し下げ)
left_mouse_down(26, 12) left_mouse_down(46, 174) left_mouse_down(370, 61) (* --間隔を空けてマウスボタンをアップ(放す)すればクリックになる
delay 0.5 left_mouse_up(370, 61) *)

(* --右クリック
right_mouse_down(261, 527) delay 0.1 right_mouse_up(261, 527) *)

on left_mouse_down(x, y) set cocoa_ruby to "require 'osx/cocoa'; event=OSX::CGEventCreateMouseEvent(nil, 1, OSX::CGPointMake(" & x & "," & y & "), 0); OSX::CGEventPost(0,event);"
set pt to do shell script "/usr/bin/ruby -e " & quoted form of cocoa_ruby
end left_mouse_down

on left_mouse_up(x, y) set cocoa_ruby to "require 'osx/cocoa'; event=OSX::CGEventCreateMouseEvent(nil, 2, OSX::CGPointMake(" & x & "," & y & "), 0); OSX::CGEventPost(0,event);"
set pt to do shell script "/usr/bin/ruby -e " & quoted form of cocoa_ruby
end left_mouse_up

on right_mouse_down(x, y) set cocoa_ruby to "require 'osx/cocoa'; event=OSX::CGEventCreateMouseEvent(nil, 3, OSX::CGPointMake(" & x & "," & y & "), 0); OSX::CGEventPost(0,event);"
set pt to do shell script "/usr/bin/ruby -e " & quoted form of cocoa_ruby
end right_mouse_down

on right_mouse_up(x, y) set cocoa_ruby to "require 'osx/cocoa'; event=OSX::CGEventCreateMouseEvent(nil, 4, OSX::CGPointMake(" & x & "," & y & "), 0); OSX::CGEventPost(0,event);"
set pt to do shell script "/usr/bin/ruby -e " & quoted form of cocoa_ruby
end right_mouse_up

  • AppleScriptからマウスボタンを押す・放す、という非常に低レベルなイベント操作まで出来てしまうのであった!
  • さらに、以下のページで紹介されているmouseClickハンドラは、あらゆるマウスイベントを発生させる。
  • mouseClickハンドラを活用すれば...
    • commandキーやoptionキーなども併用し、
    • ダブルクリック・ドリプルクリックも簡単に行えるのだ!
    • しかも、現在のマウスカーソル位置を活用することもできる!

素晴らし過ぎる!AppleScriptからここまで出来るとは!感動した!

  • 中身は、cocoaRubyを使ってCocoaの機能を利用しているのだけど、
  • AppleScriptとして配布すれば、OSX10.6標準の環境で実行できてしまうのである。
  • 余分なインストールなしに、そのまま実行できてしまう所が最大の魅力だと思う。

カーソルの座標を取得する

  • マウスカーソルを操作していると、現在のカーソル位置を活用したくなることがよくある。そんな時に...


mouse_position() --mouse_position()'s x
--mouse_position()'s y
--mouse_position() as list

--マウスカーソル位置{x:100, y:100}を返す
on mouse_position() set cocoa_ruby to "require 'osx/cocoa'; pt=OSX::NSEvent.mouseLocation; puts pt.x, OSX::NSScreen.mainScreen.frame.size.height-pt.y;"
set pt to do shell script "/usr/bin/ruby -e " & quoted form of cocoa_ruby
{x:pt's paragraph 1 as number, y:pt's paragraph 2 as number} end mouse_position

  • もはや、NS(NextStep)系の古いオブジェクトより、最新のCG(コア・グラフィック?)系のオブジェクトを利用するべきかもしれない。


--別解
getMouseLocation()
on getMouseLocation() set theRubyScript to "require 'osx/cocoa'; pt=OSX::CGEventGetLocation(OSX::CGEventCreate(nil)); puts pt.x, pt.y"
set thePtText to do shell script "/usr/bin/ruby -e " & quoted form of theRubyScript
{paragraph 1 of thePtText as number, paragraph 2 of thePtText as number} end getMouseLocation

AppleScriptであれこれする


マウスカーソルを制御して幸せな環境を作ろう!