lwouis / alt-tab-macos

Windows alt-tab on macOS
https://alt-tab-macos.netlify.app
GNU General Public License v3.0
10.94k stars 331 forks source link

After opening AltTab, it seems like the Next Window shortcut is held or pressed a few times without the user doing anything #3117

Open SamadiPour opened 9 months ago

SamadiPour commented 9 months ago

Describe the bug

Since a few weeks ago, I have been experiencing drifting when the mouse is hovered over an item. It only happens when the mouse is over an item, and not when it's inside or outside of the frame. When I hold down the Command key and press Tab once (push and release instantly), the selector drifts through the items, sometimes until it reaches the mouse and sometimes until it reaches the end. The Mouse hover option under Also select windows using is disabled.

Screenshots / video

https://github.com/lwouis/alt-tab-macos/assets/24422125/44cec264-affb-4058-b880-d87e60a9a14a

Steps to reproduce the bug

Your environment

jordantrizz commented 1 month ago

Under "System Settings->Keyboard" moving "Delay until repeat" all the way to the right effectively the highest setting 6/6 "Short" the issue appears. However at 5/6, the issue goes away.

@lwouis I think this is why you can't recreate the issue, and why only a small portion of people are facing the issue.

I could be totally wrong here.

Occurs:

image

Doesn't Occur:

image
markkkkas commented 1 month ago

Under "System Settings->Keyboard" moving "Delay until repeat" all the way to the right effectively the highest setting 6/6 "Short" the issue appears. However at 5/6, the issue goes away.

@lwouis I think this is why you can't recreate the issue, and why only a small portion of people are facing the issue.

I could be totally wrong here.

Occurs: image

Doesn't Occur: image

I can still reproduce it even with 5/6 or 0/6

xavierchen0 commented 1 month ago

I am still facing the same issue, and I believe it comes down to the mouse cursor being on the Alt-Tab ui. I am able to reproduce the bug and I will include the screen recordings and karabiner's eventviewer keypresses for the recording when the mouse cursor is on the alt-tab ui.

Environment: macOS Sonoma Version 14.6.1 Alt-Tab Version 6.70.1

Recording with mouse cursor on alt-tab ui

https://github.com/user-attachments/assets/f6cdcbd3-7419-46af-935e-038233864aaa

Karabiner's eventviewer: You can see that after command tab is pressed, a left/right arrow key is immediately pressed even though I did not press them, and only press command + tab [ { "type": "down", "name": {"key_code":"t"}, "usagePage": "7 (0x0007)", "usage": "23 (0x0017)", "misc": "" }, { "type": "up", "name": {"key_code":"t"}, "usagePage": "7 (0x0007)", "usage": "23 (0x0017)", "misc": "" }, { "type": "down", "name": {"key_code":"t"}, "usagePage": "7 (0x0007)", "usage": "23 (0x0017)", "misc": "" }, { "type": "up", "name": {"key_code":"t"}, "usagePage": "7 (0x0007)", "usage": "23 (0x0017)", "misc": "" }, { "type": "down", "name": {"key_code":"a"}, "usagePage": "7 (0x0007)", "usage": "4 (0x0004)", "misc": "" }, { "type": "up", "name": {"key_code":"a"}, "usagePage": "7 (0x0007)", "usage": "4 (0x0004)", "misc": "" }, { "type": "down", "name": {"key_code":"b"}, "usagePage": "7 (0x0007)", "usage": "5 (0x0005)", "misc": "" }, { "type": "up", "name": {"key_code":"b"}, "usagePage": "7 (0x0007)", "usage": "5 (0x0005)", "misc": "" }, { "type": "down", "name": {"key_code":"left_shift"}, "usagePage": "7 (0x0007)", "usage": "225 (0x00e1)", "misc": "flags left_shift" }, { "type": "down", "name": {"key_code":"hyphen"}, "usagePage": "7 (0x0007)", "usage": "45 (0x002d)", "misc": "flags left_shift" }, { "type": "up", "name": {"key_code":"hyphen"}, "usagePage": "7 (0x0007)", "usage": "45 (0x002d)", "misc": "flags left_shift" }, { "type": "up", "name": {"key_code":"left_shift"}, "usagePage": "7 (0x0007)", "usage": "225 (0x00e1)", "misc": "" }, { "type": "down", "name": {"key_code":"u"}, "usagePage": "7 (0x0007)", "usage": "24 (0x0018)", "misc": "" }, { "type": "up", "name": {"key_code":"u"}, "usagePage": "7 (0x0007)", "usage": "24 (0x0018)", "misc": "" }, { "type": "down", "name": {"key_code":"i"}, "usagePage": "7 (0x0007)", "usage": "12 (0x000c)", "misc": "" }, { "type": "up", "name": {"key_code":"i"}, "usagePage": "7 (0x0007)", "usage": "12 (0x000c)", "misc": "" }, { "type": "down", "name": {"key_code":"left_command"}, "usagePage": "7 (0x0007)", "usage": "227 (0x00e3)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"tab"}, "usagePage": "7 (0x0007)", "usage": "43 (0x002b)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"tab"}, "usagePage": "7 (0x0007)", "usage": "43 (0x002b)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"left_arrow"}, "usagePage": "7 (0x0007)", "usage": "80 (0x0050)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"left_arrow"}, "usagePage": "7 (0x0007)", "usage": "80 (0x0050)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"up_arrow"}, "usagePage": "7 (0x0007)", "usage": "82 (0x0052)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"up_arrow"}, "usagePage": "7 (0x0007)", "usage": "82 (0x0052)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "down", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"right_arrow"}, "usagePage": "7 (0x0007)", "usage": "79 (0x004f)", "misc": "flags left_command" }, { "type": "up", "name": {"key_code":"left_command"}, "usagePage": "7 (0x0007)", "usage": "227 (0x00e3)", "misc": "" } ]

I changed my keyboard settings to one of the solutions about reducing the "delay until repeat". However, what I found was that this only delays the jumping of windows. If you were to select your windows fast enough, i think it will seem like the bug is not occuring.

image
warrenseine commented 1 month ago

I understand this is suboptimal, but the easiest option at this point is to add a configuration option to disable the code related to key repetition, as @lwouis did in this comment. It solved the issue for everyone who tried the build.

nyanpasu64 commented 1 month ago

My working theory is that on some installations, when the mouse lies over where the ui appears, the tab key release isn't properly delivered to the widget due to focusing the wrong widget, the alt tab interface appearing after the tab key is released, or the event not being delivered to the widget. I'd have to log the events received by the app's keystroke handling code to know what's going on.

markkkkas commented 1 month ago

I understand this is suboptimal, but the easiest option at this point is to add a configuration option to disable the code related to key repetition, as @lwouis did in this comment. It solved the issue for everyone who tried the build.

The only problem with this build is that you can’t skip to the first window if you’re already on the last one.

svintit commented 1 month ago

I had this same issue, restarted alttab and now it works as expected, weird..

xavierchen0 commented 1 month ago

I had this same issue, restarted alttab and now it works as expected, weird..

I tried this. Works for a few days and then the same issue returned

svintit commented 1 month ago

I had this same issue, restarted alttab and now it works as expected, weird..

I tried this. Works for a few days and then the same issue returned

Still working for me, no restart. Seems it somehow gets into a bad state sometimes

nyanpasu64 commented 3 weeks ago

The bug is very intermittent for me but I got a related (not quite identical) occurrence on my self-built binary with logging:

@discardableResult
fileprivate func handleEvent(_ id: EventHotKeyID?, _ shortcutState: ShortcutState?, _ keyCode: UInt32?, _ modifiers: UInt32?, _ isARepeat: Bool) -> Bool {
    debugPrint("handleEvent", id ?? "nil", shortcutState ?? "nil", keyCode ?? "nil", modifiers ?? "nil", isARepeat);

This printed:

"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.down "nil" "nil" false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.up "nil" "nil" false

This implies that the app saw the Cmd keypress released before the Tab keypress was registered, yet it still brought up the AltTab UI. It never closed because the Cmd keypress never got released after the UI opened. (In this case I didn't get an endless Tab key repeat alongside the stuck-open Cmd keypress. I want to take a log of that too when it happens.)

For context, handleEvent is called from three sites (according to Xcode):

To me this is a clear race condition in event handling. (Unfortunately, like many race conditions I'm unable to reproduce it easily.)

How should we address the bug?

(Sidenote: apparently InstallEventHandler belongs to CarbonEventsCore? I didn't know modern macOS still had Carbon libraries present and supported... Should we port the program off it or not?)


I still don't know the cause of the other bug behavior where positioning the mouse where the AltTab popup would spawn, would make it not close. I have not replicated that issue with logging yet.

nyanpasu64 commented 2 weeks ago

Got a different issue where the Alt+Tab menu didn't close for like 2 seconds after releasing Cmd.

"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 512 false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.down "nil" "nil" false
"handleEvent" "nil" "nil" 33 256 false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.up "nil" "nil" false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 256 false
fderop commented 2 weeks ago

I have found a way to make this bug go away, and it probably explains why "trying $random version" also helps a lot of people — changing versions should always fix it:

going to System Settings -> Privacy & Accessibility, removing AltTab, and then re-adding it back fixes the skipping for me.

This also worked for me.

System settings -> Privacy & Accessibility -> Accessibility -> Switch AltTab off and back on.

jordantrizz commented 1 week ago

This also worked for me.

System settings -> Privacy & Accessibility -> Accessibility -> Switch AltTab off and back on.

Same, perhaps the original issue should be updated with the workarounds. Would save folks some time.

nyanpasu64 commented 3 days ago

I've found a semi-reliable way to reproduce a version of this issue:

At this point you'll get event logs like:

"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.down "nil" "nil" false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" "nil" "nil" "nil" 256 false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.up "nil" "nil" false
"handleEvent" "nil" "nil" "nil" 0 false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.down "nil" "nil" false
"handleEvent" __C.EventHotKeyID(signature: 1634497652, id: 0) AltTab.ShortcutState.up "nil" "nil" false

This indicates:

Because the popup is opened (Cmd-Tab) after the event to close the menu (!Cmd) is received, the menu never closes.

What caused the original bug?

I think the initial bug report (where "alt-tab" appears held for a second or so) is a variant where when the mouse sits above the popup while it opens, the Tab release event is delayed, and the app sees it as Alt-Tab being held for a second even if the user quickly released it. I don't know what causes "mouse sitting over Alt-Tab" to trigger shortcut processing delays on some machines sometimes, but removing AltTab from accessibility options temporarily fixes it

It's possible that (in my reproducer) the lag happens because of my logging code sending data into the XCode output log; the bug becomes harder to reproduce (I have to Alt-Tab much faster to trigger the ordering error) after clearing the log. Nonetheless the core event ordering bug is an underlying Apple footgun/bug (I don't know if they promise in-order event delivery), and the effects are consistent with behaviors I and others in this thread have observed. In particular, cycling a few windows is only explainable by AltTab.ShortcutState.up being delayed, and "menu remains open" can be explained by the same mechanism.

lwouis commented 3 days ago

Hi everyone,

Could you please test this local build?

I made the shortcut handling code more loose. It may help be more robust and close even when events arrive in the wrong order from the OS. I hope it doesn't cause regressions, so please share if you see any weird behavior.

Thank you everyone to help test this one out 🙇

Thank you @nyanpasu64 for your great analysis. Thank you for helping see this issue resolved. It's a tough one as it seems to happen only for some people and in some cases. It's probably the OS being busy in specific ways, resulting in the issue.

nyanpasu64 commented 3 days ago

The build causes a new(?) problem where when I Cmd-Tab (I rebound from Alt-Tab) to an app, then press and release Cmd, it switches to the app I most recently cmd-tabbed to.

lwouis commented 3 days ago

@nyanpasu64 thank you for testing it 🙇

I wasn't able to reproduce the issue you mention. Could you please share the sequence of inputs you did? For example:

nyanpasu64 commented 3 days ago
lwouis commented 3 days ago

I was able to reproduce with these steps. Thank you!

lwouis commented 2 days ago

If I map the sequence from your logs, I see that it not in order indeed:

cmd down cmd+tab down (from a tab down) cmd up cmd down cmd+tab up [this should have happened one row before since cmd up happened] cmd up cmd+tab down (cmd down) cmd+tab up (tab up)

What AltTab does for keyboard input is listen to 3 different API callbacks:

It never occured to me before that these 3 sources could come send events in a different order than the one that happened on the actual keyboard.

Now that I think about it, it's totally possible. These callbacks may be coming from different macOS subsystems, or uses different event queues, or receive different loads at different time.

You may think that we have a complex implementation. Why not use one source of events? That's because we support quite complex use-cases:

To support that, we have the current complex setup with 3 distinct sources of events.

I'm not sure if there is a way for us to support out-of-order events. If anyone wants to help, here's where event handling happens.

Thank you

nyanpasu64 commented 2 days ago

The worst thing is that the original bug report (alt-tab acts held) seems more like a delayed tab release (hotkey), than an event reordering (which can result from delays). So there's nothing you can do in your app to detect and compensate for that issue. At this point I want to debug the actual library code that sends hotkeys to the app (eg. breakpointing AltTab and looking at parent stack frames), but I'm not planning on that yet.

arya1106 commented 2 days ago

Perhaps I'm misunderstanding, but would it not be possible to just listen for modifier events from CGEvent.tapCreate as opposed to also listening for global shortcuts from InstallEventHandler? I believe that way you wouldn't have to worry about events being delivered out of order from different APIs.

i.e. you would check cmdDown && tabDown for cmd+tab down and cmdUp || tabUp for cmd+tab up

in which case the flow would look something like this:

cmd down tab down --- equivalent to cmd+tab down from InstallEventHandler tab up --- equivalent to cmd+tab up from InstallEventHandler cmd up

lwouis commented 2 days ago

would it not be possible to just listen for modifier events from CGEvent.tapCreate as opposed to also listening for global shortcuts from InstallEventHandler?

InstallEventHandler is used because it's the only API working during Secure Input. More info here. It's a jungle, but as a result of our complex implementation, AltTab shortcut system is extremely powerful. More than any other mac app I've seen. I've looked at how other big apps (e.g. Contexts, Witch, Alfred, Spectacles, etc) are doing it, and we support the most flexible shortcuts, and work in the most cases. That's my understanding at least.

lwouis commented 2 days ago

Hi,

Could you please test this local build?

It may just be robust enough to handle some out-of-order cases. It doesn't have the issue of the previous local build.

Could you guys please try it out of a while and see if it fixes the issue for you?

Thank you 🙇

/cc @unodgs @dottedmag @jordantrizz @AlexeyKupershtokh @warrenseine @ankushagarwal @nyanpasu64 @maximvl @problame @EskelCz @AlabasterAxe @evanhammer @saitonakamura @samuelhwilliams @jebeaudet @letalumil @Joeyn1993 @RecuencoJones @xdanik

nyanpasu64 commented 1 day ago

No issues so far, though the bug is intermittent so it's hard to say it's fixed.

I noticed that after pressing alt-tab, there's a few hundred ms delay before the popup appears? Probably harmless, possibly intentional, I think KDE delays the alt-tab menu appearing so quick presses won't flicker the screen, though the downside is you have to wait a bit before you can start scanning the list of window titles to look for one you want.

lwouis commented 1 day ago

@nyanpasu64 AltTab does exactly what you described, to avoid flickering. You can remove that delay or adjust it in Preferences > Appearance > Animations... 👍