Open tobx opened 11 months ago
Today Hammerspoon did not react again and I noticed something more:
Even if I completely quit the Hammerspoon process and start it again, the problem does not get resolved. I have to take the whole MacBook to sleep and wake it up again to resolve the problem.
Hammerspoon does not really hang when I start hyper mode. I can still use the arrow keys to move my windows around, but I cannot use any of the "letter"-keys to quick-launch applications. I can also not use option+W to exit hyper mode.
That might be important. When I am in hyper mode and press the arrow keys, they are captured by Hammerspoon and used to move windows, but the rest of the keys ("letter"-keys) are not captured and are entered in the currently active application as if they were not bound to Hammerspoon. Again, I use this extensively for days without an issue and suddenly this happens. I have not yet found any special behavior that I do different when this happens.
Are there specific apps that you're usually in when you have this problem? I'm wondering if it could be an app enabling secure input mode (a.k.a. "secure keyboard input").
Basically, an app can put the system into a mode where no other apps (like, for instance, Hammerspoon) can watch what's being typed. This normally gets turned on when you're in a password field. Some terminal apps turn it on by default too (at least iTerm2 and Apple's Terminal, though possibly not Alacritty?). And sometimes certain apps can forget to turn it off again afterwards.
I think this originally just affected hs.eventtap
s, but at some point started blocking hs.hotkey
s as well - see #2880. Some people in that thread can still get hs.hotkey
s that involve modifier keys to work, but others can't. I wonder if it's something like "ignore any hotkeys that were created after secure input was turned on (e.g. by a modal being entered)".
Try changing your hyper.entered()
function to look like this:
function hyper:entered()
if hs.eventtap.isSecureInputEnabled() then
hs.alert("⚠️ Secure Input is on. Hyper Mode commands might not work.")
end
-- The rest of this is the code you already had here:
hyperAlerts = {}
for i, screen in pairs(hs.screen.allScreens()) do
alert = hs.alert("Hyper Mode ✈", hyperStyle, screen, "")
hyperAlerts[i] = alert
end
end
If the modal only gets stuck after showing that warning message, then it's definitely secure input mode that's causing the problem. You might also want to bind Escape in the modal to an exit command, so you can at least get out of hyper mode when this happens - it sounds like Escape hotkeys still work while SIM is on.[^canttest] (Or maybe just add hyper:exit(); return
after the warning message, to immediately kick you back out of hyper mode before it gets stuck.)
[^canttest]: I'm stuck on an older system at the moment, so I'm afraid I can't test this yet.
If the app that's responsible still turns SIM off when it's done, you might be able to work around it by creating and focusing an invisible hs.webview
[^whynotcanvas] in hyper:entered()
(thus taking you out of the password field or whatever), then restoring focus to the correct window in hyper:exited()
. (Of course, all the other commands that look at hs.window.focusedWindow()
would need to be changed to work on whatever window was saved by hyper:entered()
.)
[^whynotcanvas]: Edit 11/17/2023: I originally said to use an empty hs.canvas
, because it's probably more lightweight than a webview, but Hammerspoon doesn't seem to give you a way to focus an hs.canvas
. hs.webview
s don't directly have a focus
method either, but you can at least do someWebview:hswindow():focus()
.
See also: #2897, #2858
@Rhys-T Thank you a lot already. I am pretty sure that I did not always use the same app when the issue occurred, but I will double check that and note which apps I used when the issue occurs again. 50% of me using that script is moving the terminal position, but I only use Alacritty. Input Monitoring
only shows two apps that I hardly ever use. I tried with both apps open and had no issue with my script. I added the isSecureInputEnabled
and report back if there is more information.
@Rhys-T
It took a while, but it happened again.
Are there specific apps that you're usually in when you have this problem? I'm wondering if it could be an app enabling secure input mode (a.k.a. "secure keyboard input").
I have not yet found anything that I did different or any new apps that I used. I do my day to day work and suddenly it happens.
function hyper:entered() if hs.eventtap.isSecureInputEnabled() then hs.alert("⚠️ Secure Input is on. Hyper Mode commands might not work.") end
I did this and yes is is
secure input mode
that's causing the problem. Closing apps or changing focus manually did not help. Do you know by chance any way to figure out which app is currently usingsecure input mode
?
I came across https://alexwlchan.net/2021/secure-input/ which provides a python script you can run to determine which application(s) have secure input enabled and have tested it with Terminal.
I've worked with the IOKit registry before, and this should be addable as a function to Hammerspoon, but not certain when I will get a chance; in the mean time, this should help.
I came across https://alexwlchan.net/2021/secure-input/
This is great! With python3 -c 'import getpass; getpass.getpass()'
I can now enable secure input mode and test how to handle it.
Anyway, when I enable secure input mode I cannot use my Hammerspoon script, but as soon as I switch the window focus to another window, I can use it again. This does not work in case of my described error. The Hammerspoon script does not work in this case no matter which window has focus, even not after restarting Hammerspoon.
So, now I have to wait for the next error occurrence and then try to use the mentioned python script to figure out which application is responsible for that.
@tobx Maybe try changing the code to this, so that you don't have to worry about accidentally changing the secure input state while trying to run the command? (Make sure to change the path to the find_processes_using_secure_input
script.)
function hyper:entered()
hyperAlerts = {} -- moved this up here so that I can make these alerts match yours
if hs.eventtap.isSecureInputEnabled() then
local secureInputInfo = hs.execute("/path/to/find_processes_using_secure_input") -- change this path
local msg = "⚠️ Secure Input is on. Hyper Mode commands might not work.\n"..secureInputInfo
print(msg) -- leave a copy of the message in the console, so you can still see it after the alert goes away
for i, screen in pairs(hs.screen.allScreens()) do
hyperAlerts['Secure Input '..i] = hs.alert(msg, hyperStyle, screen, "")
end
end
-- The rest of this is the code you already had here:
for i, screen in pairs(hs.screen.allScreens()) do
alert = hs.alert("Hyper Mode ✈", hyperStyle, screen, "")
hyperAlerts[i] = alert
end
end
It just happened again and the process blocking secure input is called loginwindow
. The Keyboard Maestro Wiki has some information about it here. It is basically saying that there is nothing they can do. I have not found any options to figure out which daemon is blocking secure input.
I think I will give up here. The best workaround I found is to lock the screen with control + command + Q and then login again with the fingerprint. In any case this seems not to be an Hammerspoon issue.
Yeah, I've seen lots of reports of loginwindow
supposedly having SIM turned on. Some of them suggest that loginwindow
is actually forgetting to turn it off. In others, it's actually being caused by some normal (non-daemon) app - often a password manager or web browser that the user had recently entered a password into - but the system is confused and lists it as loginwindow
instead. Quitting that other app ends up fixing the problem. So it may still be worth trying to narrow the problem down to a specific app… although the fact that locking/unlocking the screen clears it up would seem to suggest that it really is loginwindow
.
The only other thing I can think of to try is setting up a timer to watch for SIM to turn on/off and notify you, so that you can hopefully tell what's causing it that way:
local simCheckInterval <const> = 5 -- seconds
local simWasOn = false
local lastSIMApp = nil
simCheckTimer = hs.timer.doEvery(simCheckInterval, function()
local simIsOn = hs.eventtap.isSecureInputEnabled()
local simApp = simIsOn and hs.execute[[ps -c -o pid=,command= -p $(ioreg -l -w 0 | grep -Eo '"kCGSSessionSecureInputPID"=[0-9]+' | cut -d= -f2 | sort | uniq)]] or nil
if simIsOn ~= simWasOn or simApp ~= lastSIMApp then
local msg = 'Secure Input Mode is ' .. (simIsOn and 'ON' or 'OFF')
if simIsOn then
msg = msg .. '\nEnabled by:\n' .. simApp
else
msg = msg .. '\nWas enabled by:\n' .. lastSIMApp
end
msg = msg:gsub('loginwindow', 'unknown (supposedly loginwindow)')
msg = msg:gsub('^%s*(.-)%s*$', '%1')
print(msg)
for i, screen in pairs(hs.screen.allScreens()) do
hs.alert(msg, { fillColor = { hex = simIsOn and '300000' or '003000' } }, screen)
end
simWasOn = simIsOn
lastSIMApp = simApp
end
end)
You can adjust how often it checks on the first line. Smaller values will give a better idea of when it's getting turned on, but might put a bit more load on your system. It should display any changes using hs.alert
so you see it immediately, as well as print
ing them to the console so you can go back and reread it after the alert goes away.
If the app that's responsible still turns SIM off when it's done, you might be able to work around it by creating and focusing an invisible
hs.webview
2 inhyper:entered()
(thus taking you out of the password field or whatever), then restoring focus to the correct window inhyper:exited()
. (Of course, all the other commands that look aths.window.focusedWindow()
would need to be changed to work on whatever window was saved byhyper:entered()
.)
@Rhys-T Do you have the code for this workaround? My browser turns secure input mode on for password fields, and this greatly hinders me, as I have my clipboard manager open using a hyper keybinding.
@Rhys-T Do you have the code for this workaround? My browser turns secure input mode on for password fields, and this greatly hinders me, as I have my clipboard manager open using a hyper keybinding.
I haven't fully tested this, because on my system SIM doesn't interfere with hs.hotkey
, but I've tested the basic idea of having Hammerspoon steal focus to make another app turn off SIM, and it seems to work for both Firefox password fields and iTerm. In theory it should be something like this:
If hyper:entered()
detects that SIM is on, it will grab focus using the webview, then wait up to 1⁄4 second for SIM to turn off. (If it's still on at that point, it gives up and shows a warning, attempting to tell you what app has SIM enabled.) hyper:exited()
then puts focus back in the original window.
Any hyper mode commands that manipulate the current window will need to be modified to use realCurrentWindow
instead of hs.window.focusedWindow()
. Any hyper mode commands that change which window is focused should do something like:
someWindow:focus()
if focusStealingWebview:isVisible() then
focusStealingWebview:hswindow():focus() -- to make sure that the hotkeys don't stop working
end
realCurrentWindow = someWindow -- so you don't get sent back to the original window after releasing hyper
@Rhys-T I tried this. I even changed the webview to make it visible. The webview shows up correctly, but it doesn't get focused; the focus is still on the password input bar.
In the code below, I have removed the timer check; I just manually wait for almost a second myself. The focus never changes.
local focusStealingWebview = hs.webview.new{x=0, y=0, w=500, h=500}
function hyper_modality:entered()
hyper_modality.entered_p = true
-- I have not yet added the redis updaters for purple_modality.
redisActivateMode("hyper_modality")
if isSecureInputEnabled() then
realCurrentWindow = hs.window.focusedWindow()
focusStealingWebview:show():hswindow():focus()
else
realCurrentWindow = nil
end
if hyper_alert_canvas_p then
hyperModeIndicator:show()
else
-- ...
end
end
I am currently doing
local function doEscape()
hs.eventtap.keyStroke({}, "escape")
end
instead to make the password input element de-focused, and it works. It doesn't return the focus to the password element afterwards though.
@Rhys-T I tried this. I even changed the webview to make it visible. The webview shows up correctly, but it doesn't get focused; the focus is still on the password input bar.
Odd… it works on my machine. In hindsight, however, I'm not sure why it's working - if I'm understanding the docs correctly, I didn't set the webview to be able to receive keyboard focus!
Can you try adding one or both of these after the line that first creates the webview?
focusStealingWebview:allowTextEntry(true)
-- and/or
focusStealingWebview:windowStyle(hs.webview.windowMasks.titled)
Other info that might be helpful:
hs.dockIcon
setting turned off - is that correct? Can HS focus the webview if you turn that setting back on? (Shouldn't matter - I do too, and focusing the webview works either way on my machine - but I'm trying to check everything that could be affecting focus.)hs.hotkey.bind('ctrl', "'",
function() -- pressed
focusStealingWebview:show():hswindow():focus()
end,
function() -- released
focusStealingWebview:hide()
end
)
I am currently [simulating an Escape press] instead to make the password input element de-focused, and it works. It doesn't return the focus to the password element afterwards though.
Interesting. Escape doesn't normally de-focus inputs in any of the major browsers as far as I know. Are you running any sort of extension like Vimium or Tridactyl that could be adding that? In any case, if we can't get the webview approach working, de-focusing the input could be a viable alternative to de-focusing the entire window - for some apps. Doesn't seem to work in terminals, though - at least for Apple Terminal and iTerm.
local prevFocusedElement
-- in hyper_modality:entered(), if SIM is on
local axApp = hs.axuielement.applicationElement(hs.application.frontmostApplication())
if axApp then
prevFocusedElement = axApp.AXFocusedUIElement
prevFocusedElement.AXFocused = false
end
-- in hyper_modality:exited()
if prevFocusedElement and prevFocusedElement:isValid() then
prevFocusedElement.AXFocused = true
end
@Rhys-T
env:
I tried commenting
hs.dockicon.hide()
but it didn't change anything.
I tried both
focusStealingWebview:allowTextEntry(true)
-- and/or
focusStealingWebview:windowStyle(hs.webview.windowMasks.titled)
but they didn't work. The web view does get the focus IF a password input bar is not in focus.
When the web view does get the focus, it does not return it when it is hidden. The web view also has a problem of being only in the space it was created on.
I also tried on a non-browser app with a password input bar. The results were the same. Unfortunately, the escape trick does not work with non-browser apps.
The AXFocused
trick works great on the browser, but it doesn't work on that other app; when I do prevFocusedElement.AXFocused = false
, it has no effects and prevFocusedElement.AXFocused
stays true
. Is there a way I can defocus the element more forcefully?
Not sure if it would help but I take a different approach when Hammerspoon tells me that Secure Input Mode is enabled for a field: I temporarily keep a reference to the table with password info that I would normally eventtap.keyStrokes()
in, and set a timer to wipe that reference from memory.
Then when the called function sees that reference it extracts the pass phrase from the password info, sets the pasteboard contents to that, and sets another timer to clear that from the pasteboard.
Finally the characters from the pasteboard get individually eventtap.keyStroke()
-ed in, along with any modifiers, with a slight delay so that too-smart algorithms don't object to non-human input speeds.
Full disclosure-wise, there is - of course - a brief period when the password is accessible to other apps on the paste/clipboard. I chose this approach because occasionally I would run into obstinate apps that rolled their own "secure input". For them I would copy their password to the clip board and then just invoke the pasteboard character stroking via a separate key binding.
See also the example configuration for this, which is all geared toward using passwords maintained in KeyChain.
This has been working for me for years, through Ventura; I have not taken the Sonoma plunge yet. The references to modal in the discussion above, however, remind me that using ModalMgr for these key bindings broke with Big Sur and I never put in the effort to address that breakage. I switched back to not using modal key bindings for password entry. This may all be unhelpful as a result.
Hi,
I am using Hammerspoon for a few years now and I have a very hard to track down issue. Every once in a while (a week or a few days or so) my script hangs without any error messages in the Hammerspoon Console.
I am using macOS 12.6.8. I am using my script to quick launch applications and as a window manager. It works like that:
In hyper mode:
I use that script extensively every day, but only every few days or weeks when I open hyper mode, the modal shows up, but the script does not react to key strokes in hyper mode. There is no error message or any other message in the Hammerspoon Console when I press a key.
Reload config
stops the script and closes the modal window, but the issue persists when entering hyper mode again. By trial and error over the years I realized that the issue gets resolved when I put my MacBook to sleep and log in again. Then again the issue comes back sporadically. I guess it is some timing issue / race condition.I hope someone has an idea how to track down the issue when it occurs the next time. The good thing is, as soon as it appears I can reproduce it, by reloading the script until I login or reboot the next time.
Since I have no idea which part of the script is relevant for the bug, I post the full script in here: