Add mouse-keyboard via hammerspoon
parent
cc3daf4dfb
commit
95d1571e1b
@ -0,0 +1,194 @@
|
||||
-- Save to ~/.hammerspoon
|
||||
-- In ~/.hammerspoon/init.lua:
|
||||
-- local vimouse = require('vimouse')
|
||||
-- vimouse('cmd', 'm')
|
||||
--
|
||||
-- This sets cmd-m as the key that toggles Vi Mouse.
|
||||
--
|
||||
-- h/j/k/l moves the mouse cursor by 20 pixels. Holding shift moves by 100
|
||||
-- pixels, and holding alt moves by 5 pixels.
|
||||
--
|
||||
-- Pressing <space> sends left mouse down. Releasing <space> sends left mouse
|
||||
-- up. Holding <space> and pressing h/j/k/l is mouse dragging. Tapping
|
||||
-- <space> quickly sends double and triple clicks. Holding ctrl sends right
|
||||
-- mouse events.
|
||||
--
|
||||
-- <c-y> and <c-e> sends the scroll wheel event. Holding the keys will speed
|
||||
-- up the scrolling.
|
||||
--
|
||||
-- Press <esc> or the configured toggle key to end Vi Mouse mode.
|
||||
|
||||
return function(tmod, tkey)
|
||||
-- local overlay = nil
|
||||
local log = hs.logger.new('vimouse', 'debug')
|
||||
local tap = nil
|
||||
local orig_coords = nil
|
||||
local dragging = false
|
||||
local scrolling = 0
|
||||
local mousedown_time = 0
|
||||
local mousepress_time = 0
|
||||
local mousepress = 0
|
||||
local tapmods = {['cmd']=false, ['ctrl']=false, ['alt']=false, ['shift']=false}
|
||||
|
||||
if type(tmod) == 'string' then
|
||||
tapmods[tmod] = true
|
||||
else
|
||||
for _, name in ipairs(tmod) do
|
||||
tapmods[name] = true
|
||||
end
|
||||
end
|
||||
|
||||
local eventTypes = hs.eventtap.event.types
|
||||
local eventPropTypes = hs.eventtap.event.properties
|
||||
local keycodes = hs.keycodes.map
|
||||
|
||||
function postEvent(et, coords, modkeys, clicks)
|
||||
local e = hs.eventtap.event.newMouseEvent(et, coords, modkeys)
|
||||
if clicks > 3 then
|
||||
clicks = 3
|
||||
end
|
||||
e:setProperty(eventPropTypes.mouseEventClickState, clicks)
|
||||
e:post()
|
||||
end
|
||||
|
||||
tap = hs.eventtap.new({eventTypes.keyDown, eventTypes.keyUp}, function(event)
|
||||
local code = event:getKeyCode()
|
||||
local flags = event:getFlags()
|
||||
local repeating = event:getProperty(eventPropTypes.keyboardEventAutorepeat)
|
||||
local coords = hs.mouse.getAbsolutePosition()
|
||||
|
||||
if (code == keycodes.tab or code == keycodes['`']) and flags.cmd then
|
||||
-- Window cycling
|
||||
return false
|
||||
end
|
||||
|
||||
if code == keycodes.space then
|
||||
-- Mouse clicking
|
||||
if repeating ~= 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
local btn = 'left'
|
||||
if flags.ctrl then
|
||||
btn = 'right'
|
||||
end
|
||||
|
||||
local now = hs.timer.secondsSinceEpoch()
|
||||
if now - mousepress_time > hs.eventtap.doubleClickInterval() then
|
||||
mousepress = 1
|
||||
end
|
||||
|
||||
if event:getType() == eventTypes.keyUp then
|
||||
dragging = false
|
||||
postEvent(eventTypes[btn..'MouseUp'], coords, flags, mousepress)
|
||||
elseif event:getType() == eventTypes.keyDown then
|
||||
dragging = true
|
||||
if now - mousedown_time <= 0.3 then
|
||||
mousepress = mousepress + 1
|
||||
mousepress_time = now
|
||||
end
|
||||
|
||||
mousedown_time = hs.timer.secondsSinceEpoch()
|
||||
postEvent(eventTypes[btn..'MouseDown'], coords, flags, mousepress)
|
||||
end
|
||||
|
||||
orig_coords = coords
|
||||
elseif event:getType() == eventTypes.keyDown then
|
||||
local mul = 0
|
||||
local step = 20
|
||||
local x_delta = 0
|
||||
local y_delta = 0
|
||||
local scroll_y_delta = 0
|
||||
local is_tapkey = code == keycodes[tkey]
|
||||
|
||||
if is_tapkey == true then
|
||||
for name, _ in pairs(tapmods) do
|
||||
if flags[name] == nil then
|
||||
flags[name] = false
|
||||
end
|
||||
|
||||
if tapmods[name] ~= flags[name] then
|
||||
is_tapkey = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if flags.alt then
|
||||
step = 5
|
||||
end
|
||||
|
||||
if flags.shift then
|
||||
mul = 5
|
||||
else
|
||||
mul = 1
|
||||
end
|
||||
|
||||
if is_tapkey or code == keycodes['escape'] then
|
||||
if dragging then
|
||||
postEvent(eventTypes.leftMouseUp, coords, flags, 0)
|
||||
end
|
||||
dragging = false
|
||||
-- overlay:delete()
|
||||
-- overlay = nil
|
||||
hs.alert('Vi Mouse Off')
|
||||
tap:stop()
|
||||
hs.mouse.setAbsolutePosition(orig_coords)
|
||||
return true
|
||||
elseif (code == keycodes['y'] or code == keycodes['e']) and flags.ctrl then
|
||||
if repeating ~= 0 then
|
||||
scrolling = scrolling + 1
|
||||
else
|
||||
scrolling = 1
|
||||
end
|
||||
|
||||
local scroll_mul = 1 + math.log(scrolling)
|
||||
if code == keycodes['y'] then
|
||||
scroll_y_delta = math.ceil(-1 * scroll_mul)
|
||||
else
|
||||
scroll_y_delta = math.floor(1 * scroll_mul)
|
||||
end
|
||||
log.d("Scrolling", scrolling, '-', scroll_y_delta)
|
||||
elseif code == keycodes['h'] then
|
||||
x_delta = step * mul * -1
|
||||
elseif code == keycodes['l'] then
|
||||
x_delta = step * mul
|
||||
elseif code == keycodes['j'] then
|
||||
y_delta = step * mul
|
||||
elseif code == keycodes['k'] then
|
||||
y_delta = step * mul * -1
|
||||
end
|
||||
|
||||
if scroll_y_delta ~= 0 then
|
||||
hs.eventtap.event.newScrollEvent({0, scroll_y_delta}, flags, 'line'):post()
|
||||
end
|
||||
|
||||
if x_delta or y_delta then
|
||||
coords.x = coords.x + x_delta
|
||||
coords.y = coords.y + y_delta
|
||||
|
||||
if dragging then
|
||||
postEvent(eventTypes.leftMouseDragged, coords, flags, 0)
|
||||
else
|
||||
hs.mouse.setAbsolutePosition(coords)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end)
|
||||
|
||||
hs.hotkey.bind(tmod, tkey, nil, function(event)
|
||||
local screen = hs.mouse.getCurrentScreen()
|
||||
local frame = screen:fullFrame()
|
||||
|
||||
-- overlay = hs.drawing.rectangle(frame)
|
||||
-- overlay:setFillColor({['red']=0, ['blue']=0, ['green']=0, ['alpha']=0.2})
|
||||
-- overlay:setFill(true)
|
||||
-- overlay:setLevel(hs.drawing.windowLevels['assistiveTechHigh'])
|
||||
-- overlay:show()
|
||||
|
||||
hs.alert('Vi Mouse On')
|
||||
orig_coords = hs.mouse.getAbsolutePosition()
|
||||
tap:start()
|
||||
end)
|
||||
end
|
Loading…
Reference in New Issue