ラベル Hammerspoon の投稿を表示しています。 すべての投稿を表示
ラベル Hammerspoon の投稿を表示しています。 すべての投稿を表示

2025年1月1日水曜日

HammerSpoon 同一アプリで複数ウィンドウを開いている場合にそれぞれのウィンドウを制御する方法

HammerSpoon 同一アプリで複数ウィンドウを開いている場合にそれぞれのウィンドウを制御する方法

概要

アプリ名で絞り込んでからウィンドウの一覧を取得する方法もありますがプロセスとして独立してる場合にはそれだと取得できないことがあるのですべてのアプリを取得してからアプリ名でウィンドウを絞り込み操作します

環境

  • macOS 15.2
  • HammerSpoon 1.0.0

サンプルコード

hs.hotkey.bind({"cmd", "alt"}, "S", function()
    -- hs.applicatoin.find -> app:allWindows ではミューレータを検知できないのですべてのアプリを取得してからアプリ名で対象のウィンドウを絞り込む
    local activeWindows = {}
    local apps = hs.application.runningApplications()
    for _, app in ipairs(apps) do
        if app:name() == "MuMu Android Device" then
            local windows = app:allWindows()
            for i, win in ipairs(windows) do
                table.insert(activeWindows, win)
            end
        end
    end

    -- 各ウィンドウにサイズと位置を指定、左上の角が指定したX/Yの開始位置
    local width = 275
    local height = 527
    local center_device_x = 960 - (275 / 2)
    local toolbar_mergin_height = 25
    local positions = {
        -- 上段
        {x = center_device_x, y = 0, w = width, h = height},
        {x = center_device_x - width, y = 0, w = width, h = height},
        {x = center_device_x + width, y = 0, w = width, h = height},
        {x = center_device_x - (width * 2), y = 0, w = width, h = height},
        {x = center_device_x + (width * 2), y = 0, w = width, h = height},
        -- 下段
        {x = center_device_x, y = height + toolbar_mergin_height, w = width, h = height},
        {x = center_device_x - width, y = height + toolbar_mergin_height, w = width, h = height},
        {x = center_device_x + width, y = height + toolbar_mergin_height, w = width, h = height},
        {x = center_device_x - (width * 2), y = height + toolbar_mergin_height, w = width, h = height},
        {x = center_device_x + (width * 2), y = height + toolbar_mergin_height, w = width, h = height},
    }

    for i, win in ipairs(activeWindows) do
        if positions[i] then
            win:setFrame(positions[i])
        end
    end
end)

最後に

制御するウィンドウをハッシュに登録してあとからウィンドウを操作するようにしています

2024年12月29日日曜日

HammerSpoon で実行した処理を強制終了する方法

HammerSpoon で実行した処理を強制終了する方法

概要

ツールバーにある hammerspoon から reload config できるのが一番ですが実行中の hammerspoon でツールバーを選択するとクルクルになるので reload できません

またアクティビティモニターで強制終了してもいいですが hammerspoon が動いているとカーソル位置を持っていかれるので大変です

一番簡単に終了できる方法を紹介します

環境

  • macOS 15.2
  • HammerSpoon 1.0.0

強制終了方法

ターミナルで以下を実行します

  • pkill Hammerspoon

or

  • pkill -9 Hammerspoon

これが一番てっとり早いです
終了後は途中から実行したり最初に戻って再実行すれば OK です

最後に

HammerSpoon で長いタスクを実行する場合はカーソル情報が HammerSpoon に乗っ取られるので必ず強制終了する道を残しておきましょう

2024年12月28日土曜日

HammerSpoon でドラッグアンドドロップする方法

HammerSpoon でドラッグアンドドロップする方法

概要

過去 に HammerSpoon で紹介しましたがうまく動作しないケースがあるようです
その場合は event.types.leftMouseDragged イベントをうまく使うとドラッグアンドドロップできるようになります

環境

  • macOS 15.2
  • HammerSpoon 1.0.0

ドラッグアンドドロップする関数

function dragMouse(pointA, pointB, sleep)
    local event = hs.eventtap.event
    local xdiff = pointB.x - pointA.x
    local ydiff = pointB.y - pointA.y
    -- ここの割り算の3の数が少ないほどドラッグが滑らかになる
    -- 速いドラッグにしたい場合は数を大きくする
    local loop = math.floor(math.sqrt((xdiff * xdiff) + (ydiff * ydiff)) / 3)
    local xinc = xdiff / loop
    local yinc = ydiff / loop
    sleep = math.floor((sleep * 1000) / loop)
    midPoint = {x=pointA.x, y=pointA.y}
    for i = 1,loop do
        midPoint.x = midPoint.x + xinc
        midPoint.y = midPoint.y + yinc
        newPoint = {x = math.floor(midPoint.x), y = math.floor(midPoint.y)}
        hs.mouse.absolutePosition(newPoint)
        event.newMouseEvent(event.types.leftMouseDragged, newPoint):post()
        hs.timer.usleep(sleep)
    end
    newPoint = {x = math.floor(pointB.x), y = math.floor(pointB.y)}
    hs.mouse.absolutePosition(newPoint)
    event.newMouseEvent(event.types.leftMouseDragged, newPoint):post()
end

使い方

hs.hotkey.bind({"cmd", "alt"}, "T", function()
    local upSlideStartPoint = {x=500, y=500}
    local upSlideEndPoint = {x=200, y=200}
    -- カーソル位置を移動
    hs.mouse.absolutePosition(upSlideStartPoint)
    -- マウスを押した状態にする
    hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseDown, upSlideStartPoint):post()
    -- マウスを押しながら移動する
    -- 1の数字の部分を調整することでドラッグアンドドロップのスピードが変わる
    -- 数が少ないほど速くなる
    dragMouse(upSlideStartPoint, upSlideEndPoint, 1)
    -- マウスを離す
    hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseUp, upSlideEndPoint):post()
end)

最後に

ポイントはカーソルを移動した際に event.types.leftMouseDragged を実行する点かなと思います

多くのサンプルはマウスダウンをしてそのままカーソルを移動してその後マウスを上げるになっているのですがそれだとダメなようです

2024年12月27日金曜日

HammerSpoon で Mac の自動操作入門

HammerSpoon で Mac の自動操作入門

概要

Hammerspoon は Mac でショートカットキーを設定しそのショートカットキーに割り当てられたスクリプトを実行し Mac の操作を自動化できるツールです
今回はインストールからマウスクリックとスワイプ操作を実装してみました

環境

  • macOS 15.2
  • hammerspoon 1.0.0

インストール

  • brew install hammerspoon

アプリケーションの一覧に hammerspoon のアイコンがあるのでクリックして起動します

するとツールバーに hammerspoon のアイコンが常駐するようになるのでこれで起動完了です

権限の付与

システム設定 -> プライバシーとセキュリティ -> アクセシビリティ

で権限を付与してあげましょう

通知の許可

システム設定 -> 通知

から hammerspoon の通知を許可してあげましょう

Hello World

まずは Hello World です

ツールバーにある hammerspoon のアイコンをクリックし「Open config」を選択します

するとテキストエディタが起動するので以下の lua スクリプトを貼り付けましょう

hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
  hs.alert.show("Hello World!")
end)

これで準備 OK です
ツールバーのアイコンから「Reload config」しキーボードで cmd + ctrl + alt + w キーを押すと Hello World と表示されることが確認できます

ファイルの場所は

Open config で開けるファイル ~/.hammerspoon/init.lua にあります
これを直接好きなエディタで編集してから Reload config しても OK です

特定の箇所をクリックする

長いのでキーは cmd + alt + c にしています
特定の箇所を左クリックする場合は hs.eventtap.leftClick します
最後に完了を知らせるために hs.notify.new で通知していますが毎回通知が来て面倒という場合には Hello World で使った hs.alert.show を使うのがいいかなと思います

lua の場合コメントハイフン2つで表現します

hs.hotkey.bind({"cmd", "alt"}, "C", function()
    -- 座標を指定してクリック (: X=500, Y=300)
    local clickPoint = {x=500, y=300}
    
    -- マウスカーソルを指定位置に移動
    hs.mouse.absolutePosition(clickPoint)
    
    -- 左クリックイベントを発生させる
    hs.eventtap.leftClick(clickPoint)

    -- 通知でクリック完了を知らせる (任意)
    hs.notify.new({title="Hammerspoon", informativeText="Left click at (500, 300)"}):send()
end)

スワイプ(ドラッグアンドドロップ)

なぜかテキストが選択状態にはならないようですがちゃんとこれでドラッグアンドドロップできてします

newMouseEvent で leftMouseDown して押しっぱなしにしたあとに移動させて leftMouseUp させます

hs.hotkey.bind({"cmd", "alt"}, "C", function()
    -- スワイプの開始座標と終了座標を設定
    local startPoint = {x=500, y=500}
    local endPoint = {x=700, y=700}

    -- マウスカーソルを開始位置に移動
    hs.mouse.absolutePosition(startPoint)
    hs.timer.usleep(50000) -- 50ms待機

    -- マウスダウン (押しっぱなしにする)
    local mouseDown = hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseDown, startPoint)
    mouseDown:post()
    hs.timer.usleep(300000) -- 300ms待機 (長押しエミュレート)

    -- スワイプの動作 (滑らかな移動)
    local steps = 50 -- ステップ数
    for i = 1, steps do
        local progress = i / steps
        local currentPoint = {
            x = startPoint.x + (endPoint.x - startPoint.x) * progress,
            y = startPoint.y + (endPoint.y - startPoint.y) * progress
        }
        hs.mouse.absolutePosition(currentPoint)
        hs.timer.usleep(5000) -- 5ms待機 (スムーズに移動)
    end

    -- マウスアップ (クリック解除)
    local mouseUp = hs.eventtap.event.newMouseEvent(hs.eventtap.event.types.leftMouseUp, endPoint)
    mouseUp:post()
end)

以下で強制的にウィンドウをアクティブにしても選択状態にはなりませんでした
テキストを選択状態にするには別の方法があるのかもしれません

local app = hs.application.get("Google Chrome")
if app then
    app:activate() -- アプリケーションをアクティブにする
else
    hs.alert.show("Google Chrome is not running!")
end

最後に

Mac の HammerSpoon で Mac の自動操作を試してみました
ショートカットキーをフックにして動作するのでこのあたりも自動化できるとさらに便利かなと思いました

他にもいろいろと使い道がありそうなのでもう少し触ってみて追加の Tips があればまた紹介します

参考サイト