Skip to content

Conversation

@yar2000T
Copy link
Contributor

Added new option for macro and shortcuts items!

@Schneegans
Copy link
Contributor

Hi there! Thanks for the initiative! However, for me (currently testing this on GNOME / Wayland), this does not work. I suppose that it's because binding and unbinding hotkeys takes a couple of milliseconds. Maybe the binding and unbinding of hotkeys is missing an await here and there?

Also, there are a couple of interesting cases:

  • Does it work with all press-shortcut-again options? These change which shortcuts stay bound while the menu is shown.
  • Does it work with and without delayed execution?
  • Does it not interfere with the global tray-icon option to inhibit all shortcuts? If this is set, it should not be unset by executing the hotkey item...

Overall, I think it may be necessary to refactor the hotkey-inhibiting logic here. This is currently quite messy anyways. I think we would need some sort of stack of currently bound hotkeys. So we could make temporary changes to the set via a pushBoundShortcuts(shortcuts) / popBoundShortcuts(). This would make it easier to properly restore the previous state without knowing it...

@yar2000T
Copy link
Contributor Author

Hi there! Thanks for the initiative! However, for me (currently testing this on GNOME / Wayland), this does not work. I suppose that it's because binding and unbinding hotkeys takes a couple of milliseconds. Maybe the binding and unbinding of hotkeys is missing an await here and there?

Also, there are a couple of interesting cases:

* Does it work with all press-shortcut-again options? These change which shortcuts stay bound while the menu is shown.

* Does it work with and without delayed execution?

* Does it not interfere with the global tray-icon option to inhibit all shortcuts? If this is set, it should not be unset by executing the hotkey item...

Overall, I think it may be necessary to refactor the hotkey-inhibiting logic here. This is currently quite messy anyways. I think we would need some sort of stack of currently bound hotkeys. So we could make temporary changes to the set via a pushBoundShortcuts(shortcuts) / popBoundShortcuts(). This would make it easier to properly restore the previous state without knowing it...

Thanks for the detailed feedback! I’ll check all these cases and do a small refactor of the hotkey logic accordingly 👍

@yar2000T
Copy link
Contributor Author

Hello Simon! I tried to implement everything as you said, for me it worked in both cases, but i cant check this on Gnome.

@yar2000T
Copy link
Contributor Author

@Schneegans Im ready for review...

@Schneegans
Copy link
Contributor

I haven't looked into your implementation too much, but I see that you now have four memebers for the binding logic:

  private shortcuts: string[] = [];
  private inhibitedShortcuts: string[] = [];
  private boundShortcutsStack: string[][] = [];
  private inhibitedShortcutsStack: string[][] = [];

This somehow feels not easier than before. Why do we need the stacks and shortcuts and inhibitedShortcuts? Aren't the latter simply the top stack items? Do we really need two stacks? Is inhibiting a shortcut not simply pushing a new set of bound shortcuts?

I thought (but haven't spent too much time thinking here) that a single private boundShortcutsStack: string[][] = []; should be sufficient to implement this...

@yar2000T
Copy link
Contributor Author

I haven't looked into your implementation too much, but I see that you now have four memebers for the binding logic:

  private shortcuts: string[] = [];
  private inhibitedShortcuts: string[] = [];
  private boundShortcutsStack: string[][] = [];
  private inhibitedShortcutsStack: string[][] = [];

This somehow feels not easier than before. Why do we need the stacks and shortcuts and inhibitedShortcuts? Aren't the latter simply the top stack items? Do we really need two stacks? Is inhibiting a shortcut not simply pushing a new set of bound shortcuts?

I thought (but haven't spent too much time thinking here) that a single private boundShortcutsStack: string[][] = []; should be sufficient to implement this...

A single boundShortcutsStack: string[][] alone is not generally sufficient, because some backends (e.g., KDE Wayland) represent inhibition as a separate logical state (they cannot unbind shortcuts at the OS level). However, I can simplify to a single stack of paired state

@yar2000T
Copy link
Contributor Author

I just pushed an example of this logic—maybe this is what you had in mind for me to do?

@Schneegans
Copy link
Contributor

Schneegans commented Jan 2, 2026

I think the logic is still too complex and over-engineered here. The involved backend methods and members currently are:

  • stateStack
  • get currentFrame()
  • bindShortcuts()
  • inhibitShortcuts()
  • inhibitAllShortcuts()
  • getBoundShortcuts()
  • getInhibitedShortcuts()
  • pushBoundShortcuts()
  • popBoundShortcuts()
  • bindShortcutsImpl()
  • inhibitShortcutsImpl()

Let's take a step back and think about what is really necessary here. And you are right, we have to consider that some backends cannot silently unbind shortcuts, so we definitely need the inhibition mechanism.

So when are shortcuts really (re-)bound?

  • When program starts
  • When user edits menus

Do we need a stack here? I do not think so. So a single string[] should be sufficient to keep track of the currently bound shortcuts.

Then there is inhibiting shortcuts. When does this happen?

  • When a menu is shown and the press-same-shortcut-again policy is set to "nothing". Then the shortcut of the opened menu is inhibited.
  • When the tray-icon option to disable shortcuts is used.
  • (new) when hotkeys or macros are simulated.

These inhibition scenarios can all happen simultaneously, so we need to make sure our implementation can handle that. So for instance, the tray-icon option to disable shortcuts could be enabled, then a menu is opened which inhibits the shortcut of the opened menu, even if it's already inhibited by the tray-icon option. Then, a hotkey or macro could be simulated, which would also inhibit the shortcut of the opened menu. Then, all parts of the implementation will undo their inhibitions again. But as the tray-icon option is still checked, the shortcut should remain inhibited.

Our first idea was to go for a stack-based approach. So anyone can push and pop inhibited shortcuts. At any point in time, the effectively bound shortcuts will be "all bound shortcuts MINUS the union of all pushed stack items".

If we go for this stack-based approach, it would be necessary that all inhibition-sources are undone in the correct order, which could be possible, but may be hard to enforce in the presence of async methods. So if I think about this, this comes to my mind:

Action Bound Shortcuts Inhibited-Shortcuts Stack Effectively Bound Shortcuts Notes
Kando Starts Ctrl+A, Ctrl+B Ctrl+A, Ctrl+B We have two menus in this example.
Menu A is opened Ctrl+A, Ctrl+B Ctrl+A Ctrl+B If menu A is opened, its shortcut is inhibited, so only menu B is still bound.
A complex macro is simulated Ctrl+A, Ctrl+B Ctrl+A
Ctrl+A, Ctrl+B
A macro item is clicked, all Kando shortcuts are inhibited. So the stack now contains two items.
Menu A is closed Ctrl+A, Ctrl+B Ctrl+A Ctrl+B Macro execution is still ongoing, but the menu is already closed. So the last stack entry was popped, yet this was the wrong one! Ctrl+B is bound again even if the macro is still being executed!
Macro execution finished Ctrl+A, Ctrl+B Ctrl+A, Ctrl+B Now all entries are popped, so everything is bound again.

As you can see from the example above, on second thought, a stack for inhibited shortcuts may not be ideal. So what can we do about this?

One idea would be to abandon the stack approach and go for an inhibition-source based approach. So there would be a single inhibitShortcuts(shortcuts: string[]): number method which returns an inhibition-id. Then there would be a releaseInhibition(id: number): void method to release the inhibition again. Internally, we would keep a inhibitedShortcuts: Map<number, string[]> map of inhibition-ids to inhibited-shortcut-arrays. Then, when calculating the effectively bound shortcuts (probably in bindShortcutsImpl), we would take "all bound shortcuts MINUS the union of all inhibited-shortcut-arrays in the map". Some backends - like the KDE backend - can do this differently and decide to just ignore all the inhibited shortcuts.

So every time inhibitShortcuts is called, a new inhibition-id is generated and the corresponding inhibited-shortcut-array is stored in the map. When releaseInhibition is called, the entry is removed from the map. This way, we can easily manage multiple inhibition sources which do not have to be released in a specific order.

What do you think about this approach?
Should I try to implement this and see how it works out?

This reverts commit 0b81bd1.
@yar2000T
Copy link
Contributor Author

yar2000T commented Jan 2, 2026

I think the inhibition-source based approach sounds reasonable and addresses the ordering issues you described. I don’t have a concrete implementation in mind yet, but I’m happy to explore this direction and see how it works out in practice. I’ll follow up with a draft implementation or questions if I get stuck, I guess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants