mirror of
https://github.com/jimeh/dotfiles.git
synced 2026-02-19 11:46:40 +00:00
148 lines
4.6 KiB
Lua
148 lines
4.6 KiB
Lua
--- === PushToTalk ===
|
|
---
|
|
--- Implements push-to-talk and push-to-mute functionality with `fn` key.
|
|
--- I implemented this after reading Gitlab remote handbook https://about.gitlab.com/handbook/communication/ about Shush utility.
|
|
---
|
|
--- My workflow:
|
|
---
|
|
--- When Zoom starts, PushToTalk automatically changes mic state from `default`
|
|
--- to `push-to-talk`, so I need to press `fn` key to unmute myself and speak.
|
|
--- If I need to actively chat in group meeting or it's one-on-one meeting,
|
|
--- I'm switching to `push-to-mute` state, so mic will be unmute by default and `fn` key mutes it.
|
|
---
|
|
--- PushToTalk has menubar with colorful icons so you can easily see current mic state.
|
|
---
|
|
--- Sample config: `spoon.SpoonInstall:andUse("PushToTalk", {start = true, config = { app_switcher = { ['zoom.us'] = 'push-to-talk' }}})`
|
|
--- and separate keybinding to toggle states with lambda function `function() spoon.PushToTalk.toggleStates({'push-to-talk', 'release-to-talk'}) end`
|
|
---
|
|
--- Check out my config: https://github.com/skrypka/hammerspoon_config/blob/master/init.lua
|
|
|
|
local obj = {}
|
|
obj.__index = obj
|
|
|
|
-- Metadata
|
|
obj.name = "PushToTalk"
|
|
obj.version = "0.1"
|
|
obj.author = "Roman Khomenko <roman.dowakin@gmail.com>"
|
|
obj.homepage = "https://github.com/Hammerspoon/Spoons"
|
|
obj.license = "MIT - https://opensource.org/licenses/MIT"
|
|
|
|
obj.defaultState = 'unmute'
|
|
|
|
obj.state = obj.defaultState
|
|
obj.pushed = false
|
|
--- PushToTalk.app_switcher
|
|
--- Variable
|
|
--- Takes mapping from application name to mic state.
|
|
--- For example this `{ ['zoom.us'] = 'push-to-talk' }` will switch mic to `push-to-talk` state when Zoom app starts.
|
|
obj.app_switcher = {}
|
|
|
|
local function showState()
|
|
local device = hs.audiodevice.defaultInputDevice()
|
|
local muted = false
|
|
if obj.state == 'unmute' then
|
|
obj.menubar:setIcon(hs.spoons.resourcePath("speak.pdf"))
|
|
elseif obj.state == 'mute' then
|
|
obj.menubar:setIcon(hs.spoons.resourcePath("muted.pdf"))
|
|
muted = true
|
|
elseif obj.state == 'push-to-talk' then
|
|
if obj.pushed then
|
|
obj.menubar:setIcon(hs.spoons.resourcePath("record.pdf"), false)
|
|
else
|
|
obj.menubar:setIcon(hs.spoons.resourcePath("unrecord.pdf"))
|
|
muted = true
|
|
end
|
|
elseif obj.state == 'release-to-talk' then
|
|
if obj.pushed then
|
|
obj.menubar:setIcon(hs.spoons.resourcePath("unrecord.pdf"))
|
|
muted = true
|
|
else
|
|
obj.menubar:setIcon(hs.spoons.resourcePath("record.pdf"), false)
|
|
end
|
|
end
|
|
|
|
device:setMuted(muted)
|
|
end
|
|
|
|
function obj.setState(s)
|
|
obj.state = s
|
|
showState()
|
|
end
|
|
|
|
obj.menutable = {
|
|
{ title = "UnMuted", fn = function() obj.setState('unmute') end },
|
|
{ title = "Muted", fn = function() obj.setState('mute') end },
|
|
{ title = "Push-to-talk (fn)", fn = function() obj.setState('push-to-talk') end },
|
|
{ title = "Release-to-talk (fn)", fn = function() obj.setState('release-to-talk') end },
|
|
}
|
|
|
|
local function appWatcher(appName, eventType, appObject)
|
|
local new_app_state = obj.app_switcher[appName];
|
|
if (new_app_state) then
|
|
if (eventType == hs.application.watcher.launching) then
|
|
obj.setState(new_app_state)
|
|
elseif (eventType == hs.application.watcher.terminated) then
|
|
obj.setState(obj.defaultState)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function eventTapWatcher(event)
|
|
device = hs.audiodevice.defaultInputDevice()
|
|
if event:getFlags()['fn'] then
|
|
obj.pushed = true
|
|
else
|
|
obj.pushed = false
|
|
end
|
|
showState()
|
|
end
|
|
|
|
--- PushToTalk:init()
|
|
--- Method
|
|
--- Initial setup. It's empty currently
|
|
function obj:init()
|
|
end
|
|
|
|
--- PushToTalk:init()
|
|
--- Method
|
|
--- Starts menu and key watcher
|
|
function obj:start()
|
|
self:stop()
|
|
obj.appWatcher = hs.application.watcher.new(appWatcher)
|
|
obj.appWatcher:start()
|
|
|
|
obj.eventTapWatcher = hs.eventtap.new({hs.eventtap.event.types.flagsChanged}, eventTapWatcher)
|
|
obj.eventTapWatcher:start()
|
|
|
|
obj.menubar = hs.menubar.new()
|
|
obj.menubar:setMenu(obj.menutable)
|
|
obj.setState(obj.state)
|
|
end
|
|
|
|
--- PushToTalk:stop()
|
|
--- Method
|
|
--- Stops PushToTalk
|
|
function obj:stop()
|
|
if obj.appWatcher then obj.appWatcher:stop() end
|
|
if obj.eventTapWatcher then obj.eventTapWatcher:stop() end
|
|
if obj.menubar then obj.menubar:delete() end
|
|
end
|
|
|
|
--- PushToTalk:toggleStates()
|
|
--- Method
|
|
--- Cycle states in order
|
|
---
|
|
--- Parameters:
|
|
--- * states - A array of states to toggle. For example: `{'push-to-talk', 'release-to-talk'}`
|
|
function obj:toggleStates(states)
|
|
new_state = states[1]
|
|
for i, v in pairs(states) do
|
|
if v == obj.state then
|
|
new_state = states[(i % #states) + 1]
|
|
end
|
|
end
|
|
obj.setState(new_state)
|
|
end
|
|
|
|
return obj
|