Skip to content

Instantly share code, notes, and snippets.

@kizzx2
Last active December 19, 2022 06:47
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kizzx2/e542fa74b80b7563045a to your computer and use it in GitHub Desktop.
Save kizzx2/e542fa74b80b7563045a to your computer and use it in GitHub Desktop.
Hammerspoon script to move/resize window under cursor
-- Inspired by Linux alt-drag or Better Touch Tools move/resize functionality
function get_window_under_mouse()
-- Invoke `hs.application` because `hs.window.orderedWindows()` doesn't do it
-- and breaks itself
local _ = hs.application
local my_pos = hs.geometry.new(hs.mouse.getAbsolutePosition())
local my_screen = hs.mouse.getCurrentScreen()
return hs.fnutils.find(hs.window.orderedWindows(), function(w)
return my_screen == w:screen() and my_pos:inside(w:frame())
end)
end
dragging_win = nil
dragging_mode = 1
drag_event = hs.eventtap.new({ hs.eventtap.event.types.mouseMoved }, function(e)
if dragging_win then
local dx = e:getProperty(hs.eventtap.event.properties.mouseEventDeltaX)
local dy = e:getProperty(hs.eventtap.event.properties.mouseEventDeltaY)
local mods = hs.eventtap.checkKeyboardModifiers()
-- Ctrl + Shift to move the window under cursor
if dragging_mode == 1 and mods.ctrl and mods.shift then
dragging_win:move({dx, dy}, nil, false, 0)
-- Alt + Shift to resize the window under cursor
elseif mods.alt and mods.shift then
local sz = dragging_win:size()
local w1 = sz.w + dx
local h1 = sz.h + dy
dragging_win:setSize(w1, h1)
end
end
return nil
end)
flags_event = hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(e)
local flags = e:getFlags()
if flags.ctrl and flags.shift and dragging_win == nil then
dragging_win = get_window_under_mouse()
dragging_mode = 1
drag_event:start()
elseif flags.alt and flags.shift and dragging_win == nil then
dragging_win = get_window_under_mouse()
dragging_mode = 2
drag_event:start()
else
drag_event:stop()
dragging_win = nil
end
return nil
end)
flags_event:start()
@feifanzhou
Copy link

Thank you all — this is exactly what I've been looking for!

@harob
Copy link

harob commented Nov 27, 2019

I've been happily using this for years, but now that I've upgraded to OSX Catalina it's extremely laggy. The window moves with delay relative to cursor movement, and the modifier keys seem to stay activated for a few seconds after unpressing them. Has anyone else encountered this or found a fix?

@joaofnds
Copy link

Hi guys, I just found out about hammerspoon. After spending a weekend on linux I found myself trying to alt+leftClick to drag and alt+rightClick to resize, so I went looked for a solution and here I am :)

Here's what I did based on the script I found here. This implements cmd + leftClick for moving and cmd + rightClick for resizing.

function get_window_under_mouse()
  local pos = hs.geometry.new(hs.mouse.getAbsolutePosition())
  local screen = hs.mouse.getCurrentScreen()

  return hs.fnutils.find(hs.window.orderedWindows(), function(w)
    return screen == w:screen() and pos:inside(w:frame())
  end)
end

dragging_window = nil

drag_event = hs.eventtap.new(
  {
    hs.eventtap.event.types.leftMouseDragged,
    hs.eventtap.event.types.rightMouseDragged,
  }, function(e)
    if not dragging_win then return nil end

    local dx = e:getProperty(hs.eventtap.event.properties.mouseEventDeltaX)
    local dy = e:getProperty(hs.eventtap.event.properties.mouseEventDeltaY)
    local mouse = hs.mouse:getButtons()

    if mouse.left then
      dragging_win:move({dx, dy}, nil, false, 0)
    elseif mouse.right then
      local sz = dragging_win:size()
      local w1 = sz.w + dx
      local h1 = sz.h + dy
      dragging_win:setSize(w1, h1)
    end
end)

flag_event = hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(e)
  local flags = e:getFlags()

  if flags.cmd then
    dragging_win = get_window_under_mouse()
    drag_event:start()
  else
    draggin_win = nil
    drag_event:stop()
  end
end)

flag_event:start()

As @harob has said, hammerspoon is getting a little still, I don't know why. If one of you finds a solution, share it with us! :)

@dbalatero
Copy link

Regarding slow FPS when resizing, check out this (closed) issue. Sounds like it needs to be fixed internally somehow: Hammerspoon/hammerspoon#923

@dbalatero
Copy link

Hey all,

I created a spoon that combines some of the techniques here and fixes the slow framerate while resizing:

https://github.com/dbalatero/SkyRocket.spoon

Instead of resizing the window in realtime, I draw an transparent preview box on the screen with hs.canvas, resize that as you drag, then commit the actual resize to the window when you let go of the mouse. It's a compromise for sure, but it's way better than dealing with the laggy framerate when naively trying to resize the window in realtime.

@arrelid
Copy link

arrelid commented Nov 15, 2021

@dbalatero Thanks for the workaround - works like a charm!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment