Files
hammerspoon/monocle.lua
T
francop b6bdc7fcc0 revert 6f9c62f3f4
revert Tested sync
2026-05-14 19:50:08 -04:00

129 lines
3.4 KiB
Lua

-- ~/.hammerspoon/Monocle.lua
local Monocle = {}
-- CONFIGURATION
local blurRadius = 20
local dimAlpha = 0.3
local shakeTrigger = 250 -- Energy required (higher = harder shake)
local overlay = nil
local filter = nil
local mouseTimer = nil
local clickWatcher = nil
local energy = 0
function Monocle.show()
local screen = hs.screen.mainScreen()
local res = screen:fullFrame()
local win = hs.window.focusedWindow()
if not win then return end
if not overlay then
overlay = hs.canvas.new(res)
overlay:level(101)
overlay:behavior({
hs.canvas.windowBehaviors.canJoinAllSpaces,
hs.canvas.windowBehaviors.ignoresMouseEvents
})
-- Layer 1: Background Dimming
overlay[1] = {
type = "rectangle",
action = "fill",
fillColor = { black = 1, alpha = dimAlpha },
frame = { x = 0, y = 0, w = "100%", h = "100%" },
}
-- APPLY THE BLUR FILTER TO THE WHOLE CANVAS
overlay:setFilter({
name = "CIGaussianBlur",
inputRadius = blurRadius
})
end
overlay:frame(res)
overlay:show()
win:raise()
end
function Monocle.hide()
if overlay then overlay:hide() end
energy = 0
end
function Monocle.toggle()
if overlay and overlay:isShowing() then
Monocle.hide()
else
Monocle.show()
end
end
function Monocle.init()
-- 1. HYPERKEY BINDING
if hyper then
hs.hotkey.bind(hyper, "f", function()
Monocle.toggle()
end)
end
-- 2. Focus Follower
filter = hs.window.filter.new(nil)
filter:subscribe(hs.window.filter.windowFocused, function()
if overlay and overlay:isShowing() then
Monocle.show()
end
end)
-- 3. Click Outside to Disable
clickWatcher = hs.eventtap.new({1}, function(event)
if overlay and overlay:isShowing() then
local clickPoint = event:location()
local win = hs.window.focusedWindow()
if win then
local f = win:frame()
local isInside = (clickPoint.x >= f.x and clickPoint.x <= (f.x + f.w) and
clickPoint.y >= f.y and clickPoint.y <= (f.y + f.h))
if not isInside then
Monocle.hide()
end
end
end
return false
end):start()
-- 4. Vector-Based Shake Detection
local lastPos = hs.mouse.absolutePosition()
local lastDx, lastDy = 0, 0
mouseTimer = hs.timer.doEvery(0.02, function()
local newPos = hs.mouse.absolutePosition()
local dx = newPos.x - lastPos.x
local dy = newPos.y - lastPos.y
-- If current direction (dx) is opposite of last direction (lastDx)
-- That indicates a shake reversal.
local reversedX = (dx * lastDx < 0)
local reversedY = (dy * lastDy < 0)
if reversedX or reversedY then
local speed = math.sqrt(dx*dx + dy*dy)
if speed > 15 then
energy = energy + (speed * 2)
end
else
-- Constant decay (15 units per cycle)
energy = math.max(0, energy - 15)
end
if energy > shakeTrigger then
Monocle.toggle()
energy = 0
end
lastDx, lastDy = dx, dy
lastPos = newPos
end)
end
return Monocle