lwouis / alt-tab-macos

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

Problems with AltGr key on external keyboard #511

Closed MaralWa closed 4 years ago

MaralWa commented 4 years ago

I use a Microsoft Ergonomic Sculpt Keyboard as an external keyboard for my MacBook. The keyboard has an 'AltGr' key. The pipe symbol '|' is located on the '<' key, see attached picture. To enter the pipe symbol I have to press the combination 'AltGr + <'. But when AltTab is active that combination does not produce the pipe symbol. Instead I can cycle through the windows of the current application. The attached screenshot was taken when I wrote this issue and presed 'AltGr + <' to enter the pipe symbol '|' while AltTab was running. In order to be able to enter the pipe symbo I had to quit AltTab.

I consider this a show stopper bug because as long as AltTab I cannot enter pipe commands like 'ls | sort | uniq' in any shell programm like iTerm2. Hence AltTab is unusable for me.

I observe a second issue which is not as severe as the first one.

When AltTab is running you can cycle forward through the open application windows with 'Alt + Tab'. To cycle backwards I expected to use the combination 'Shift + Alt + Tab' but works only once in stead I have to type 'Shift + Alt', i.e. without the Tab-key which is quite counter intuitive.

62ADE4F9-FD3F-4FE0-8977-FA37717F6476_1_105_c

alttab_cycle

lwouis commented 4 years ago

Hi @MaralWa! Thanks for sharing this feedback!

I thought it would be easier to record a reply, so here it is ๐Ÿ‘

lwouis commented 4 years ago

@MaralWa any update?

PLanB2008 commented 4 years ago

Just one addition after looking into the record:

You're right about the key mapping issue, but this is by default using the german keyboard layout on macos. Even triggering the stock switch window of application command by default is the "wrong" key, assuming the right key would be the key above the TAB key. On a german keyboard this button without any modifier triggers ^. So the intended way for a german keyboard would be something like this:

grafik (having ^ as the switch key). This said I don't think its really necessary to map this by default the right way when even stock macos gets it wrong.

edit: The second question is also solvable by using shift + tab for the "select previous window" as I configured it for myseld in the shown screenshot.

lwouis commented 4 years ago

I researched and found that Processing use localization tech to decide which shortcuts to use. I think it's not good as they tie language with keyboard layout, which is correlated but not always 1-to-1. A lot of people use apps in English, on US-layout keyboard, in many countries, for instance. Thus I think I will keep the logic of finding which shortcut best suits a keyboard layout separated from language.

@Kentzo do you know how shortcuts work across international keyboards? For example, it seems that ShortcutRecorder as the same defaults shortcuts for all keyboards for all the macOS built-in shortcuts. Are these not different on different keyboards? I tested by putting my Input Source in Bangla and I see that cmd+c and cmd+v work the same, even though the keyboard viewer shows this:

Screen Shot 2020-08-27 at 14 48 34

Pressing the c key on my US-layout keyboard shows this on this website:

image

I wonder if that's the same on a physical Bangla keyboard, or if it's a consequent of me virtualizing it.

I'm kind of lost in the complexity here. I think for AltTab we want the key above tab as the default shortcut, because it's ergonomically sound. However, I don't know how to achieve that. I couldn't find any library providing this feature. I thought I would implement it myself, but I'm quite confused with where to find keyboards of the world. For example, if I switch my Input Source to German or German - Standard (what's even the difference), my layout doesn't look like the one @MaralWa has, or @PLanB2008 talks about. This is what I get:

Screen Shot 2020-08-27 at 14 53 16

Above my tab key is > not ^.

@MaralWa @PLanB2008 could you please share what you see on the website tool when you press the key above tab on your keyboards?

As reference, this is the code that AltTab uses currently to guess the key above tab:

LiteralKeyCodeTransformer.shared.transformedValue(NSNumber(value: kVK_ANSI_Grave)) ?? "`"

Here is some nice discussion around Apple keyboard technology. Based on the picture, it would seem that there is no way to somehow convert a universal value to all the local variants. The only approach would be to have a big map of InputSource -> Key above tab

lwouis commented 4 years ago

Actually, I remember the solution I used working in my tests in the past:

LiteralKeyCodeTransformer.shared.transformedValue(NSNumber(value: kVK_ANSI_Grave)) ?? "`"

I tested again, and it seems to works fine for German, French, Arabic, etc.

I think I get the issue now: you're using a Windows keyboard. On Windows I guess the keyboards layout have some changes. I see this project for example, remapping windows to mac on a German Windows keyboard.

So it seems that the current status of things is that AltTab probably smartly adapts its default shortcut on most mac keyboards, but fails when it's a Windows keyboard. I can imagine it also fails on custom keyboards with exotic layouts.

What do you guys think? Should we try and support more environments, or is this reasonable and we close the ticket, and people who get a wrong default shortcut can remap it to something better?

PLanB2008 commented 4 years ago

Another quick addition:

The default German keyboard has 105 keys. The US keyboard 102 I think. This is also true for some other country specific keyboards. This could be the reason your arrangement looked different.

In my opinion this is not such a big deal because it's user configurable.

lwouis commented 4 years ago

Ok let's close the issue then ๐Ÿ‘

Kentzo commented 4 years ago

@Kentzo do you know how shortcuts work across international keyboards?

The idea is that a given combination of physical keyboard keys for a given selected layout source can be mapped both into a natural symbol and its ASCII equivalent, e.g. "ั„" and "a" for KeyCode.ansiA using Russian layout input source.

The latter "ASCII" equivalent is selected by macOS and in my experience matches the duality of physical keys imprints (on a typical QWERTY Russian keyboard Russian letter "ั„" and Latin letter "a" are on the same physical key). That translation is, of course, subject to a selected input source, installed drivers and whatnot switches on a keyboard itself.

While macOS (Cocoa's key equivalents) does support registering shortcuts via non-ASCII characters (e.g. "ั„") and can properly display them (as "ั„") doing so is impractical because as soon as you change your layout input source back to, say, U.S. English these shortcuts will stop working for the same physical keyboard keys. But it does work the other way around, i.e. the "a" shortcut will still respond to "ั„" when you switch to Russian layout input source.

I guess it's easy to compute an ASCII equivalent for a given non-ASCII character while it's rather hard to verify all possible non-ASCII equivalents for a given ASCII character and thus macOS does only the former during shortcut matching.

I thus suggest to use ASCIILiteralKeyCodeTransformer in place of LiteralKeyCodeTransformer as the result will be more consistent.

I hope it makes sense, see also TISCopyCurrentKeyboardLayoutInputSource and TISCopyCurrentASCIICapableKeyboardLayoutInputSource functions.

lwouis commented 4 years ago

Hey @Kentzo! Thank you for sharing this!

I think I understand you explanation about how shortcuts work with multiple Input Sources. Now the problem I was trying to solve specifically in this ticket was: what's the string representation of the key physically above tab, on the current Input Source. Using that value, we can set a default for the US shortcut alt+` that feels ergonomic to users which use a keyboard or an Input Source that has another key above tab.

I did some tests to compare results of ASCIILiteralKeyCodeTransformer and LiteralKeyCodeTransformer, by changing my Input Source to various languages. Here are some cases were the results differed:

Input Source LiteralKeyCodeTransformer ASCIILiteralKeyCodeTransformer Input Source preview image
Russian ] < image
Russian - PC ะ ` image
Japanese ` | < image

These empirical results seem to indicate to me that LiteralKeyCodeTransformer returns the correct key compared to ASCIILiteralKeyCodeTransformer.

What do you think? Maybe I'm missing something here

Kentzo commented 4 years ago

I think I understand you explanation about how shortcuts work with multiple Input Sources.

It's not directly related to shortcuts, it's the input system. Shortcuts are only affected when a match needs to be made using characters and not key codes, e.g. with Cocoa's menu item key equivalent.

what's the string representation of the key physically above tab, on the current Input Source

Do I understand you correctly that you need this only for visual rendering and the value does not participate in any matching otherwise? If so, then LiteralKeyCodeTransformer may indeed be a better solution.

lwouis commented 4 years ago

Do I understand you correctly that you need this only for visual rendering and the value does not participate in any matching otherwise?

The app launches, and this code is run:

LiteralKeyCodeTransformer.shared.transformedValue(NSNumber(value: kVK_ANSI_Grave)) ?? "`"

This provides a string representation of the-key-above-tab. That string representation is fed to the SR Shortcut constructor. The Shortcut instance is then used both to show the user the value in the Preferences UI, and also if they don't customize the value, actually match the shortcut if it is hit.

Kentzo commented 4 years ago

LiteralKeyCodeTransformer.shared.transformedValue ends up calling TISCopyCurrentKeyboardLayoutInputSource every time it's performed:

  1. https://github.com/Kentzo/ShortcutRecorder/blob/07b065085e172d85b7b5a0f3cc05f6ed47ad5af1/Sources/ShortcutRecorder/SRKeyCodeTransformer.m#L919
  2. https://github.com/Kentzo/ShortcutRecorder/blob/07b065085e172d85b7b5a0f3cc05f6ed47ad5af1/Sources/ShortcutRecorder/SRKeyCodeTransformer.m#L930
  3. https://github.com/Kentzo/ShortcutRecorder/blob/07b065085e172d85b7b5a0f3cc05f6ed47ad5af1/Sources/ShortcutRecorder/SRKeyCodeTransformer.m#L117
  4. https://github.com/Kentzo/ShortcutRecorder/blob/07b065085e172d85b7b5a0f3cc05f6ed47ad5af1/Sources/ShortcutRecorder/SRKeyCodeTransformer.m#L124
  5. https://github.com/Kentzo/ShortcutRecorder/blob/07b065085e172d85b7b5a0f3cc05f6ed47ad5af1/Sources/ShortcutRecorder/SRKeyCodeTransformer.m#L172
  6. https://github.com/Kentzo/ShortcutRecorder/blob/07b065085e172d85b7b5a0f3cc05f6ed47ad5af1/Sources/ShortcutRecorder/SRKeyCodeTransformer.m#L158

Thus its result may change between runs with respect to the currently active input source and in even runtime.

lwouis commented 4 years ago

This is fine. AltTab doesn't support complex scenarios like the user having different Input Sources with different sets of shorcuts, or shortcuts adapting to changes in the Input Source.

The current approach is trying to have a the best UX while keeping these scenarios not fully supported as that would require a disproportionate amount of work and complexity to fully support.

LiteralKeyCodeTransformer is only called at launch time. So at that time, we can suggest an appriopriate shortcut based on the user's current Input Source. If later they restart the app, and have changed Input Source, then they get a new, now-more-appropriate default shortcut. If they decide to customize the shortcut, it is now set in stone, and will no longer vary between runs. I think it is a reasonable strategy that covers a lot of use-cases while being cheap compared to re-architecturing to support exotic multi-input-source workflows.

Kentzo commented 4 years ago

Yeah, that seems reasonable given that you involve the Option (Alt) key as the only modifier by default.

The only step forward I can think of is to grab current input source via TISCopyCurrentKeyboardLayoutInputSource then get its ID via TISGetInputSourceProperty and based on that give some other input-source specific default.