Spoons icon indicating copy to clipboard operation
Spoons copied to clipboard

[VolumeScroll] spoon does not work.

Open Leftium opened this issue 6 months ago • 1 comments

The VolumeScroll spoon has several problems:

  • When started via spoon.VolumeScroll.start()
    • *** ERROR: .../leftium/.hammerspoon/Spoons/VolumeScroll.spoon/init.lua:64: attempt to index a nil value (local 'self')
  • Starting this way works: spoon.VolumeScroll.start({ "cmd", "alt"}) But now on mouse scroll:
    • ERROR: LuaSkin: hs.eventtap callback error: .../leftium/.hammerspoon/Spoons/VolumeScroll.spoon/init.lua:49: attempt to call a nil value (method 'sameMods')
  • So I removed all the checks for modifiers to see how the volume control part works...
    • The volume control works for a while, but the event tap eventually just stops getting events.
    • Also the volume control felt "mushy." I think 1% volume steps feel too small?

Detailed steps:

  1. Downloaded VolumeScroll.spoon.zip from https://www.hammerspoon.org/Spoons/VolumeScroll.html
  2. Extracted zip file; double-clicked VolumeScroll.spoon to install.
  3. Added these lines to my ~/.hammerspoon/init.lua:
hs.loadSpoon("VolumeScroll")
spoon.VolumeScroll.start({ "cmd", "alt"})

Environment:

  • MacOS 15.5 (24F74)
  • Hammerspoon version 1.0.0 (6864)

Leftium avatar Jun 30 '25 21:06 Leftium

Here is a similar Spoon I wrote for myself (with assistance from AI; my first Lua script):

  • Simply start with hs.loadSpoon("VolumeControl")
  • SHIFT + scroll to adjust volume
  • SHIFT + middle mouse to toggle mute
  • Small scrolls for small volume steps, large scrolls for large steps
  • Avoids the problem where event tap stops receiving events

I was hoping to use VolumeScroll as a model for re-writing this as a good spoon (like is it bad events are set up in :init?), but this just works:

-- ~/.hammerspoon/Spoons/VolumeControl.spoon/init.lua
print("🔧 VolumeControl.spoon loading…")

local obj   = {}
obj.__index = obj

local types = hs.eventtap.event.types
local props = hs.eventtap.event.properties

local sendKey = function(key)
    hs.eventtap.event.newSystemKeyEvent(key, true ):post()
    hs.eventtap.event.newSystemKeyEvent(key, false):post()
    print(string.format('Sent key event: %s', key))                                                
end


function obj:init()
    print("🔧 VolumeControl:init() called")

    -- SHIFT+scroll → adjust volume 
    self.scrollTap = hs.eventtap.new({ types.scrollWheel }, function(evt)
        local flags = evt:getFlags()

        if not flags.shift then return false end

        local axis = props.scrollWheelEventDeltaAxis2 or
                     props.scrollWheelEventDeltaAxis1

        local delta = evt:getProperty(axis)
        if     delta > 0 then sendKey('SOUND_UP')
        elseif delta < 0 then sendKey('SOUND_DOWN') end

        return true
    end):start()

    -- SHIFT + middle-click → MUTE
    self.muteTap = hs.eventtap.new({ types.otherMouseUp }, function(evt)
        if not evt:getFlags().shift or
           evt:getProperty(props.mouseEventButtonNumber) ~= 2
        then return false end

        sendKey('MUTE')
        return true
    end):start()
end

return obj

Leftium avatar Jun 30 '25 21:06 Leftium