Last active
August 17, 2023 22:44
-
-
Save Madd0g/57e7f7fcbc0170b5b8392532b2db9780 to your computer and use it in GitHub Desktop.
Hammerspoon noises-based morse code trigger (short / medium / long hisses)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local mod = {} | |
| -- thresholds in ms | |
| local MED_THRESHOLD = 150 | |
| local LNG_THRESHOLD = 540 | |
| local HIDE_MARKERS_AFTER_MATCH_SECONDS = 1 | |
| -- never really consider events older than this | |
| local EVENTS_CUTOFF = 2500 | |
| -- remember to place longer sequences ahead of short sequences | |
| -- the cases are checked in order | |
| mod.morseActions = { | |
| -- { pat = {'sh', 'me', 'lo'}, fn = function() hs.alert.show("looooo and behooold") end}, | |
| -- { pat = {'sh', 'me', 'sh'}, fn = function() showSong() end}, | |
| -- { pat = {'sh', 'me', 'me'}, fn = winTrack.report}, | |
| -- { pat = {'sh', 'sh'}, fn = function() clickNotification() end}, | |
| -- { pat = {'sh'}, fn = function() modal.toggleScroll() end}, | |
| { | |
| pat = {'sh', 'sh'}, | |
| fn = function() | |
| myDoKeyStroke({}, 'j') | |
| hs.timer.doAfter(0.25, readSelectedReddit) | |
| end | |
| }, | |
| } | |
| local start = nil | |
| local ev = {} | |
| function slice(tbl, s, e) | |
| local pos, new = 1, {} | |
| for i = s, e do | |
| new[pos] = tbl[i] | |
| pos = pos + 1 | |
| end | |
| return new | |
| end | |
| function mod.startTracking() | |
| start = hs.timer.absoluteTime() / 1e6 | |
| end | |
| function mod.trackEvent(customDuration) | |
| if start == nil and not customDuration then return end | |
| local elapsed | |
| if customDuration then | |
| elapsed = customDuration | |
| else | |
| local ts = hs.timer.absoluteTime() / 1e6 | |
| elapsed = math.floor(ts - start) | |
| end | |
| if elapsed > 1800 then return end | |
| local type = nil | |
| if elapsed > LNG_THRESHOLD then | |
| type = 'lo' | |
| elseif elapsed < MED_THRESHOLD then | |
| type = 'sh' | |
| else | |
| type = 'me' | |
| end | |
| -- log.w('elapsed:', elapsed, type) | |
| local event = {ts = ts, type = type} | |
| table.insert(ev, event) | |
| -- log.w('registering', event.type) | |
| mod.clearOldEvents() | |
| mod.draw() | |
| mod.checkIfMatch() | |
| start = nil | |
| end | |
| local con = nil | |
| local markers = {} | |
| local markerTimer = nil | |
| local function createConsole() | |
| con = console.new({ | |
| baseFont = {name = "Fira Code", size = 14}, | |
| location = {x = 0, y = 'top'}, | |
| padding = 4, | |
| cornerRadius = 5, | |
| backgroundColor = {alpha = 1, white = .03}, | |
| backgroundBorder = {alpha = .90}, | |
| initialText = '...' | |
| }) | |
| end | |
| function hidemarkers(andClearEvents) | |
| for k, obj in pairs(markers) do | |
| local v = obj.marker | |
| v:hide(0.2) | |
| end | |
| markers = {} | |
| if andClearEvents then ev = {} end | |
| end | |
| function flashMarkers(numMatched, individual) | |
| for k, obj in pairs(markers) do | |
| local v = obj.marker | |
| if #markers - k < numMatched then | |
| local oldSize = hs.geometry.new(v:frame()) | |
| local size = hs.geometry.new(v:frame()) | |
| size:scale(1.17) | |
| v:setFrame({x = size.x, y = size.y, w = size.w, h = size.h}) | |
| hs.timer.doAfter(.20, function() v:setFrame(oldSize) end) | |
| end | |
| end | |
| end | |
| local sizes = {sh = 25, me = 35, lo = 45} | |
| local colors = { | |
| sh = {["red"] = 0, ["blue"] = 0, ["green"] = 1, ["alpha"] = 0.8}, | |
| me = {["red"] = 0.3, ["blue"] = 0.8, ["green"] = 0.7, ["alpha"] = 0.8}, | |
| lo = {["red"] = 1, ["blue"] = 0.2, ["green"] = 0.2, ["alpha"] = 0.8} | |
| } | |
| function mod.draw() | |
| local frame = hs.screen.mainScreen():fullFrame() | |
| if markerTimer ~= nil then markerTimer:stop() end | |
| -- hidemarkers() | |
| local lastTop = 0 | |
| for k, v in pairs(ev) do if #ev - k < 3 then end end | |
| for k, v in pairs(ev) do | |
| if #ev - k < 3 then | |
| local size = sizes[v.type] | |
| local currColor = colors[v.type] | |
| if lastTop == 0 then | |
| lastTop = frame.h - 60 | |
| else | |
| lastTop = lastTop - 5 - size | |
| end | |
| local rect = hs.geometry.rect(30, lastTop, 20, size) | |
| local marker = hs.drawing.rectangle(rect) | |
| marker:setFillColor(currColor):setRoundedRectRadii(7, 7):show() | |
| table.insert(markers, {marker = marker, color = currColor}) | |
| -- flashMarkers(1, true) | |
| end | |
| end | |
| markerTimer = hs.timer.doAfter(2, function() hidemarkers(true) end) | |
| end | |
| function mod.checkIfMatch() | |
| -- if #ev < 2 then | |
| -- return | |
| -- end | |
| if #ev > 5 then | |
| -- log.w('too many events, ignoring everything') | |
| return | |
| end | |
| for index, v in pairs(mod.morseActions) do | |
| -- if #v.pat > 1 and v.fn then | |
| if v.fn then | |
| local isMatch = #ev >= #v.pat and | |
| checkVariation(v.pat, slice(ev, math.max( | |
| #ev - #v.pat + 1, | |
| 1), #ev)) | |
| if isMatch then | |
| flashMarkers(#v.pat) | |
| ev = {} | |
| start = nil | |
| -- delay the hide trigger 1 sec | |
| if markerTimer ~= nil then | |
| markerTimer:setNextTrigger(HIDE_MARKERS_AFTER_MATCH_SECONDS) | |
| end | |
| v.fn() | |
| return | |
| end | |
| end | |
| end | |
| end | |
| function checkVariation(variation, events) | |
| -- log.w('checking', events[1].type, '=', variation[1], 'and', events[2].type, '=', variation[2]) | |
| for k, v in pairs(events) do | |
| -- log.w('isEq', v.type, variation[k]) | |
| if v.type ~= variation[k] then return false end | |
| end | |
| return true | |
| end | |
| function mod.clearOldEvents() | |
| local ts = hs.timer.absoluteTime() / 1e6 | |
| local sliceIndex = 0 | |
| local lastTs = 0 | |
| for k, v in pairs(ev) do | |
| -- print('from last ' .. hs.inspect(v) | |
| if v ~= nil and v.ts ~= nil then | |
| local diff = ts - v.ts | |
| if diff < EVENTS_CUTOFF then break end | |
| lastTs = v.ts | |
| sliceIndex = k | |
| end | |
| end | |
| if sliceIndex > 0 then | |
| -- log.w('unpacking from', sliceIndex + 1, #ev) | |
| ev = slice(ev, sliceIndex + 1, #ev) | |
| end | |
| end | |
| return mod |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| local morse = require('morse_code') | |
| local clickState | |
| local mousepoint | |
| local point | |
| function holdMouse() | |
| clickState = hs.eventtap.event.properties.mouseEventClickState | |
| mousepoint = hs.mouse.getAbsolutePosition() | |
| point = {x=mousepoint.x, y=mousepoint.y} | |
| hs.eventtap.event.newMouseEvent(hs.eventtap.event.types["leftMouseDown"], point):setProperty(clickState, 1):post() | |
| end | |
| function releaseMouse() | |
| clickState = hs.eventtap.event.properties.mouseEventClickState | |
| mousepoint = hs.mouse.getAbsolutePosition() | |
| point = {x=mousepoint.x, y=mousepoint.y} | |
| hs.eventtap.event.newMouseEvent(hs.eventtap.event.types["leftMouseUp"], point):setProperty(clickState, 1):post() | |
| end | |
| local mod = {} | |
| function mod.start() | |
| noisesListener:start() | |
| end | |
| function stopNoiseListen() | |
| noisesListener:stop() | |
| hs.alert.show("stop listening") | |
| end | |
| function mod.stop() | |
| stopNoiseListen() | |
| end | |
| local noiseCallback = { | |
| -- mouse hold/click | |
| -- [1] = holdMouse, | |
| -- [2] = releaseMouse, | |
| -- morse code | |
| [1] = morse.startTracking, | |
| [2] = morse.trackEvent, | |
| [3] = function() | |
| -- hs.alert.show("you did it!") | |
| end, | |
| } | |
| -- local mode = 'click' | |
| local mode = nil -- morse | |
| function mod.callback(type) | |
| if type == 'scroll' then | |
| -- noiseCallback[1] = function() autoScroll.toggleAutoScroll() end | |
| -- noiseCallback[2] = function() end | |
| elseif type == 'click' then | |
| noiseCallback[1] = holdMouse | |
| noiseCallback[2] = releaseMouse | |
| else | |
| noiseCallback[1] = morse.startTracking | |
| noiseCallback[2] = morse.trackEvent | |
| end | |
| end | |
| mod.callback(mode) | |
| noisesListener = hs.noises.new(function(noiseType) | |
| noiseCallback[noiseType]() | |
| end) | |
| hs.hotkey.bind(cah, "y", function() | |
| hs.alert.show("listening") | |
| noisesListener:start() | |
| end) | |
| hs.hotkey.bind(cah, "u", stopNoiseListen) | |
| return mod |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment