Hammerspoon
The following is a script to enable drag scroll for a trackball:
-- HANDLE SCROLLING WITH MOUSE BUTTON PRESSED
-- Use button right click
-- Use button 3 for kensington back button
-- Use button 6 for Elecom Deft Pro
local config = {
scrollButtons = { 3, 6 },
rightButtonScrollEnabled = true,
scrollSpeedMultiplier = -4, -- Negative for traditional scrolling behavior
}
local oldmousepos = {}
local deferred = false
local rightHeld = false
local leftHeld = false
local sentForwardClick = false
local sentMiddleClick = false
local sentBackClick = false
local shiftHeld = false
overrideOtherMouseDown = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDown }, function(e)
--print("down")
local pressedMouseButton = e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber'])
-- print ("pressed mouse down:" .. pressedMouseButton)
if hs.fnutils.contains(config.scrollButtons, pressedMouseButton) then
-- print ("pressed mouse down:" .. pressedMouseButton .. " deferred")
deferred = true
return true
end
return false
end)
overrideOtherMouseUp = hs.eventtap.new({ hs.eventtap.event.types.otherMouseUp }, function(e)
--print("up")
local pressedMouseButton = e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber'])
-- print ("pressed mouse up:" .. pressedMouseButton)
if hs.fnutils.contains(config.scrollButtons, pressedMouseButton)
then
if (deferred) then
overrideOtherMouseDown:stop()
overrideOtherMouseUp:stop()
hs.eventtap.otherClick(e:location(), 0, pressedMouseButton)
overrideOtherMouseDown:start()
overrideOtherMouseUp:start()
return true
end
return false
end
return false
end)
overrideRightMouseDown = hs.eventtap.new({ hs.eventtap.event.types.rightMouseDown }, function(e)
rightHeld = true
-- print(modifiers)
if shiftHeld then
print "shift is held down"
print("sending button 4 (forward)")
hs.eventtap.otherClick(e:location(), 0, 4)
sentForwardClick = true
deferred = false
return true
end
-- print("Result:", rightHeld, leftHeld)
if (rightHeld and leftHeld) then
print("Sending middle click")
sentMiddleClick = true
hs.eventtap.middleClick(e:location(), 0)
deferred = false
return true
end
if (config.rightButtonScrollEnabled) then
-- print("right down")
deferred = true
-- print("Result:", rightHeld, leftHeld)
return true
end
return false
end)
overrideLeftMouseDown = hs.eventtap.new({ hs.eventtap.event.types.leftMouseDown }, function(e)
leftHeld = true
if shiftHeld then
-- print "shift is held down"
print("sending button 3 (back)")
hs.eventtap.otherClick(e:location(), 0, 3)
sentBackClick = true
deferred = false
return true
end
--print("Left Down Result:", rightHeld, leftHeld)
if (rightHeld and leftHeld) then
hs.eventtap.middleClick(e:location(), 0)
sentMiddleClick = true
deferred = false
return true
end
return false
end)
overrideRightMouseUp = hs.eventtap.new({ hs.eventtap.event.types.rightMouseUp }, function(e)
-- print("right up")
rightHeld = false
sentMiddleClick = false
if sentForwardClick then
-- print("doing nothing sent forward click")
sentForwardClick = false
deferred = false
return true
end
if (config.rightButtonScrollEnabled and deferred) then
deferred = false
-- print("Was deferred restarting handlers")
overrideRightMouseDown:stop()
overrideRightMouseUp:stop()
print("sending rightClick")
hs.eventtap.rightClick(e:location(), 0)
overrideRightMouseDown:start()
overrideRightMouseUp:start()
return true
end
return false
end)
overrideLeftMouseUp = hs.eventtap.new({ hs.eventtap.event.types.leftMouseUp }, function(e)
--print("left up")
leftHeld = false
sentBackClick = false
sentMiddleClick = false
return false
end)
dragOtherToScroll = hs.eventtap.new({ hs.eventtap.event.types.otherMouseDragged }, function(e)
local pressedMouseButton = e:getProperty(hs.eventtap.event.properties['mouseEventButtonNumber'])
-- print ("pressed mouse " .. pressedMouseButton)
if hs.fnutils.contains(config.scrollButtons, pressedMouseButton) then
-- print("scroll");
deferred = false
oldmousepos = hs.mouse.absolutePosition()
local dx = e:getProperty(hs.eventtap.event.properties['mouseEventDeltaX'])
local dy = e:getProperty(hs.eventtap.event.properties['mouseEventDeltaY'])
local scroll = hs.eventtap.event.newScrollEvent({-dx * config.scrollSpeedMultiplier, -dy * config.scrollSpeedMultiplier},{},'pixel')
--put the mouse back
hs.mouse.absolutePosition(oldmousepos)
return true, {scroll}
else
return false, {}
end
end)
dragRightToScroll = hs.eventtap.new({ hs.eventtap.event.types.rightMouseDragged }, function(e)
-- print("scroll");
deferred = false
oldmousepos = hs.mouse.absolutePosition()
local dx = e:getProperty(hs.eventtap.event.properties['mouseEventDeltaX'])
local dy = e:getProperty(hs.eventtap.event.properties['mouseEventDeltaY'])
local scroll = hs.eventtap.event.newScrollEvent({-dx * config.scrollSpeedMultiplier, -dy * config.scrollSpeedMultiplier},{},'pixel')
-- put the mouse back
hs.mouse.absolutePosition(oldmousepos)
return true, {scroll}
end)
local function shiftDownCatcher(event)
local flags = event:getFlags()
local rawFlags = event:getRawEventData().CGEventData.flags & 0xdffffeff
-- print("------------------------")
-- print("keyDump","rawFlags",rawFlags)
-- print("keyDump","regularFlags:",hs.inspect(event:getFlags()))
-- print("keyDump","keyCodzse",event:getKeyCode())
-- print("keyDump","getCharacters",event:getCharacters())
-- print("keyDump","getRawEventData",hs.inspect(event:getRawEventData()))
-- print("keyDump","systemKey",hs.inspect(event:systemKey()))
-- print("keyDump","getType",event:getType())
if flags.shift and not shiftHeld then
print("Detected shift down")
shiftHeld = true
end
return false -- Passes the event on
end
local function shiftUpCatcher(event)
local flags = event:getFlags()
if not flags.shift and shiftHeld then
print("Detected shift up")
shiftHeld = false
end
return false -- Passes the event on
end
shiftDown = hs.eventtap.new({hs.eventtap.event.types.flagsChanged}, shiftDownCatcher)
shiftUp = hs.eventtap.new({hs.eventtap.event.types.flagsChanged}, shiftUpCatcher)
overrideRightMouseDown:start()
overrideRightMouseUp:start()
overrideLeftMouseDown:start()
overrideLeftMouseUp:start()
overrideOtherMouseDown:start()
overrideOtherMouseUp:start()
dragOtherToScroll:start()
dragRightToScroll:start()
shiftDown:start()
shiftUp:start()