Hama Blog

Hama Blog

主にtech関連の記録

Macのディスプレイ間のマウスカーソル移動をショートカットで実現する ※環境制約あり

[※ 2023/9/12 追記]

この記事で書いた内容よりももっと簡単に、環境制約もなく実現できる方法があったため、以下で紹介しました。

なので基本的には以下の記事を参照していただいたほうがよいかと思います。

hama-engineer.hatenablog.com

概要

普段、Macbook を外部ディスプレイ1台に接続して使っていて、ディスプレイ間のマウスカーソル移動が地味に面倒だったので、AppleScript と Raycast を使って任意のショートカットキーで移動できるようにしてみた。

個人的には満足なものができたが、環境的な制約があるなどいろいろと課題はあるので気が向いたら対応したい。

AppleScript はドキュメント(Introduction to AppleScript Overview)がわかりにくく、関連記事もあまりなかったのですが、Apple Scriptを使ってマウスカーソルを移動させる : Lunar Eclipse をめちゃくちゃ参考にさせていただきました🙏

記事執筆時の環境

  • ディスプレイの解像度

    • Macbook Pro 2018: 1440 x 900

    • 外部ディスプレイ: 1920 x 1080

  • macOS Monterey: 12.6.5

  • AppleScript: 2.8

  • Raycast: 1.50.2

成果物(適用方法)

  • Raycast の Create Script CommandAppleScript (scpt ファイル) を登録する。

    ※ Template に AppleScript が出てこない場合は、一旦 Bash で作成して、そこから後述の AppleScript を呼び出すようにしてみてもいいかもしれない。

  • 作成された scpt ファイル(meta 情報の下)に以下のコードを貼り付け、DISPLAY_RESOLUTIONS, NEW_MOUSE_LOCATIONS の値を自分用にカスタマイズする。

      use AppleScript version "2.8"
      use framework "AppKit"
    
      set currentMouseLocation to current application's NSEvent's mouseLocation()
      set currentMouseX to x of currentMouseLocation
    
      -- Macbook:          1440 x 900
      -- External Display: 1920 x 1080
      set DISPLAY_RESOLUTIONS to {{1440, 900}, {1920, 1080}}
      set NEW_MOUSE_LOCATIONS to {{720, 380}, {2400, 540}}
    
      set firstDisplayWidth to item 1 of item 1 of DISPLAY_RESOLUTIONS
      if currentMouseX > firstDisplayWidth then
        set displayName to "Display1"
        set newMouseX to item 1 of item 1 of NEW_MOUSE_LOCATIONS
        set newMouseY to item 2 of item 1 of NEW_MOUSE_LOCATIONS
      else
        set displayName to "Display2"
        set newMouseX to item 1 of item 2 of NEW_MOUSE_LOCATIONS
        set newMouseY to item 2 of item 2 of NEW_MOUSE_LOCATIONS
      end if
    
      my moveTo(newMouseX, newMouseY)
    
      on moveTo(newX, newY)
        set point to current application's CGPointZero
        set x of point to newX
        set y of point to newY
    
        current application's CGPostMouseEvent(point, 1, 1, 0)
      end moveTo
    
      -- どのディスプレイに移動しているかわかるように最後に出力
      displayName
    
  • Raycast Preferences - Extensions で Hotkey に任意のキーを割り当てると完了。
    以下は Control + Tab を設定。

  • 以下のように動作する。
    (gif を取るために同一ディスプレイ間での移動にしている)

作った経緯

ディスプレイ間のマウスカーソル移動ができないか探していたところ、一応既存アプリで S2Mouse(有料) とか Multi Display Cursor Jump(有料) で実現できるみたいだったのでとりあえず S2Mouse を入れてみたが、ショートカットキーが固定されているっぽく個人的に困ったので、どうせならと思い今回自作してみた。

AppleScript の解説

set currentMouseLocation to current application's NSEvent's mouseLocation()
set currentMouseX to x of currentMouseLocation


-- Macbook:          1440 x 900
-- External Display: 1920 x 1080
set DISPLAY_RESOLUTIONS to {{1440, 900}, {1920, 1080}}
set NEW_MOUSE_LOCATIONS to {{720, 380}, {2400, 540}}
  • DISPLAY_RESOLUTIONS で自分の環境に合わせて各ディスプレイの解像度を定義する。
    (今回だと別に縦幅はなくてもいい)

  • NEW_MOUSE_LOCATIONS は移動後の座標を好きな値で定義する。



set firstDisplayWidth to item 1 of item 1 of DISPLAY_RESOLUTIONS
if currentMouseX > firstDisplayWidth then
  set displayName to "Display1"
  set newMouseX to item 1 of item 1 of NEW_MOUSE_LOCATIONS
  set newMouseY to item 2 of item 1 of NEW_MOUSE_LOCATIONS
else
  set displayName to "Display2"
  set newMouseX to item 1 of item 2 of NEW_MOUSE_LOCATIONS
  set newMouseY to item 2 of item 2 of NEW_MOUSE_LOCATIONS
end if
  • firstDisplayWidth (メインディスプレイ: 今回だと Macbook の横幅) を基準に currentMouseX (現在のx座標) によってどちらのディスプレイに移動するか決定する。


my moveTo(newMouseX, newMouseY)

on moveTo(newX, newY)
  set point to current application's CGPointZero
  set x of point to newX
  set y of point to newY

  current application's CGPostMouseEvent(point, 1, 1, 0)
end moveTo
  • CGPostMouseEvent 関数を使って指定した座標にマウスを瞬間移動させる。

    • CGPostMouseEvent は Deprecated になっているみたいだが、少なくとも今回の用途では問題なさそうだったのと、推奨されている CGEventCreateMouseEvent の使い方がいまいちわからなかった。

      CGPostMouseEvent | Apple Developer Documentation

      This function is not recommended for general use because of undocumented special cases and undesirable side effects. The recommended replacement for this function is CGEventCreateMouseEvent, which allows you to create a mouse event and customize the event before posting it to the event system.



-- どのディスプレイに移動しているかわかるように最後に出力
displayName
  • 以下のようにマウスが Display1Display2 のどちらにいるかがわかるようにしている。

課題

  • 解像度が変わったらコードも変えないといけない。

  • デュアルディスプレイ、かつ配置が左右でないと使えない。

  • ディスプレイを指定できず、交互にしか移動できない。

  • CGPostMouseEvent 関数が Deprecated になっている。(今回の用途では問題なさそう)

所感

最初はもっと簡単にできそうかと思ったけど、AppleScript は今回初めて触ったのと、あまりドキュメントを読み込まずに作ったので、書き方や使い方の正解がわからないまま妥協して作ってしまった。

心に余裕があるときにまた調べてみようと思う。

あと、JavaScript でも代替できそうだったが、マウスイベントを操作するなら AppleScript のほうがいいのかな〜と思っている。