Issues with keyboard layout switches
Hello,
Someone reported an issue with switching keyboard layout between Finnish and US with AltTab. I debugged and ended up pinning the issue to a simple call to the SRShortcut initializer.
With keyboard layout set to US:
Shortcut(keyEquivalent: shortcutString)
// prints: Optional(;)
With keyboard layout set to Finnish:
Shortcut(keyEquivalent: shortcutString)
// prints: nil
When checking on the Inspector app, it seems that the keyEquivalent is ; when keyboard layout is set to Finnish:
The initializer returning nil seems like a bug to me, but maybe I'm misunderstanding something.
It also seems to me that the SRRecorderControl should also not show ; after the keyboard layout switches to Finnish. The other values show Ö. I think it should show that. It actually shows it dimmed if the user clicks to set the control:

What do you think?
Thank you 🙇♂️
Hi @Kentzo, If you have time, could you please look at this issue? It looks like it could be a quick-win since it's specific and I've narrowed it down already. Thank you 🙇
@lwouis Will try to work on this before end of the month.
@lwouis Were you able to reproduce the issue with the Inspector.app? In your app, does Shortcut Recorder work in the "compatibility" mode?
Can you place a breakpoint at https://github.com/Kentzo/ShortcutRecorder/blob/c86ce0f9be5353ba998966121c7631602a9a36f7/Sources/ShortcutRecorder/SRShortcut.m#L179 and then follow the instantiation backtrace extracting arguments along the way? That would help.
Hi @Kentzo,
Thank you for your support!
I don't think I'm using "compatibility mode". I didn't know this existed. Looking at this repo, it seems to be about compatibility with SR2. I don't set any compatibility flag in AltTab.
Were you able to reproduce the issue with the Inspector.app?
Actually, no! There is the issue I detailed above about seeing ; instead of seeing Ö, on the UI. However, the Shortcut initializer doesn't return nil in the Inspector app, unlike in my app.
Here's the test code I used in both apps:
if let inputSource = TISCopyInputSourceForLanguage("fi" as CFString)?.takeRetainedValue() {
TISSelectInputSource(inputSource)
}
print(Shortcut(keyEquivalent: ";") ?? "nil")
if let inputSource = TISCopyInputSourceForLanguage("en" as CFString)?.takeRetainedValue() {
TISSelectInputSource(inputSource)
}
print(Shortcut(keyEquivalent: ";") ?? "nil")
In my app, it prints:
nil
;
In the Inspector app, it prints:
;
;
My app also uses version 3.1 of SR, plus 1 commit. I just tested, and that commit's little change was the source of the issue.
You may remember 2 years ago, we discussed https://github.com/Kentzo/ShortcutRecorder/issues/143. The ticket is actually still open. Users of AltTab found the fix in the commit above to work for them, so I forked SR and added that little fix, so I could release it to fix their issue. It seems that this fix is not proper, and introduces this regression on Finnish keyboards.
Do you have advice on how to move forward?
Perhaps upgrading to the latest version of SR would fix both issues?
Thank you 🙇
It would help to know the initialization sequence and the arguments that are passed around as you follow backtrace.
Of course! Here are the values of the args, when doing it in English Input Source
In the Finnish case, it doesn't reach as deep. It returns nil early here:
I can see how the +[Shortcut shortcutWithKeyEquivalent:] initializer can return nil for ";" under Finnish input source: the translator cannot deduce which key code corresponds to ";" as there is no such key (AFAIK) on the physical keyboard with Finnish layout.
But that's not how I expect users of RecorderControl to store its value on disk. The expected method is either via Cocoa bindings or NSSecureCoding, both of which preserve keyCode and modifierFlags thus allowing to restore the value regardless of the input source.
Is this how you actually store shortcut values on disk in your app?
Thank you for your message
When we need to save a RecorderControl, we store its .stringValue. And we we launch and need to set its saved value, we update its objectValue to Shortcut(keyEquivalent: shortcutStringSavedOnDisk)
The reason we do this, is that it's very useful to directly see the character in the preferences. I have asked users many many times to send me a print of their defaults so that I know which preferences they have set, including the shortcuts. Then I can see the literal shortcut characters.
Thank you
In that case this is a non-standard approach and you're hitting the case where it doesn't work due to the limitation of key equivalents.
Can you store both NSSecureCoding-encoded (for actual storage) and stringValue (for debugging) in the user defaults?
Thank you @Kentzo
That's an interesting idea. I'll look into this.
There is something I don't quite understand. Instantiating Shortcut(keyEquivalent: ";") works on 3.1. As I mentioned, we have made a small change in our fork, to fix this issue. This change seems to have the downside of breaking Shortcut(keyEquivalent: ";").
Isn't there a path where we fix https://github.com/Kentzo/ShortcutRecorder/issues/143 differently, and don't regression on Shortcut(keyEquivalent: ";")?
Thank you