Mac key binding question for my different Free42/Plus42 skin

163 views
Skip to first unread message

MickM

unread,
Sep 24, 2023, 9:14:53 AM9/24/23
to Free42 & Plus42
I have a skin I'm hoping to release very soon and, because it has a very different layout to the default 42S, it's revealed some issues with the key bindings for the desktop version of the skin. For starters, I'm trying to get the Mac desktop skin working and I'll then move forward with other platforms. I'm trying to use the Option modifier (or Alt if you like) for some of the functions, but they do not behave in the same way as the comments in the keymap.txt file located in:
     /Applications/Plus42\ Decimal.app/Contents/Resources/keymap.txt
In that file it says:

# Format: [Ctrl|Alt|Shift]* <Character> : <macro>
and
# NOTE: Key events that translate to a single character in the printable ASCII
# range (32..126) are matched without regard of the state of the Shift key --
# so you don't have to use "Shift A" to match uppercase "A", etc. For other key
# events, shift *is* relevant (e.g. "Return" is not the same as "Shift Return").

If I wanted to define a key mapping for e.g. Alt y and also Alt Y I was expecting to be able to do:

  MacKey: Alt y :  074
  MacKey: Alt Y : 28 075

but, after much fumbling in the dark, it turns out I need to provide the actual hex code for the unicode character that each sequence yields (in addition to also then redundantly providing the Alt identifier on the MacKey line) i.e. the unicode for the character "¥" that is produced when I type ⌥y (option-y) is 0x00A5. So for the above example to work I actually need to use:

  MacKey: Alt       0x00A5 :    074     # y^x (⌥y is "¥")
  MacKey: Alt Shift 0x00C1 : 28 075     # %   (⌥Y is "Á")

That would all be fine (although cryptic) however on the Mac that doesn't always work because it's native option characters aren't all unique i.e. Alt-n and Alt-N both yield the same character, namely "˜" which is unicode 0x02DC. The consequence is that Alt-n and Alt-N can not be distinguished from each other e.g. when I attempted to do the following only e^x is recognized:

  MacKey: Alt       0x02DC :    098     # LN (Natural LOG) (⌥n is "˜")
  MacKey: Alt Shift 0x02DC :    099     # e^x  (⌥N is "˜")

If the MacKey lines conformed to the first group at the top of my email (in accordance with the documentation) I think everything would be fine. The thing preventing me from releasing my skin is the inability to distinguish between ⌥n and ⌥N, and also ⌥i and ⌥I. Can anybody help be overcome this?

Thomas Okken

unread,
Sep 25, 2023, 5:53:07 AM9/25/23
to Free42 & Plus42
The problem with Opt-n and Opt-i is that they are dead keys, i.e. they are meant to be combined with another character, which is typed next. Opt-N and Opt-I are the non-dead versions of ˜ and ˆ.

The keyboard handling code in Free42 and Plus42 for Mac checks the keyboard event, and looks at the character being typed, that is, [theEvent characters]. It looks at this, rather than [theEvent keyCode], in order to be independent of the keyboard layout. Ususally, this does what's needed, but in the case of dead keys, [theEvent characters] returns an empty string, and the keyboard handling code ignores such events.

It would be possible to deal with this by allowing the keyboard map to specify key codes in addition to key characters, but that would cause keyboard maps using that feature to not be layout-independent.

Another option would be to use [theEvent charactersByApplyingModifiers:] to find out what the event's key character would be if the Shift state were inverted. That would make it possible to handle dead keys without losing layout independence, but it would probably require some changes to the way keymap entries are matched. I'll have to play with this a bit, stay tuned...

Thomas Okken

unread,
Sep 25, 2023, 6:44:37 AM9/25/23
to Free42 & Plus42
Yep, with this change...

 - (void)keyDown:(NSEvent *)theEvent {
     if ([theEvent isARepeat])
         return;
-    calc_keydown([theEvent characters], [theEvent modifierFlags], [theEvent keyCode]);
+    NSString *characters = [theEvent characters];
+    if ([characters length] == 0) {
+        if (@available(macOS 10.15, *)) {
+            characters = [theEvent charactersByApplyingModifiers:[theEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask ^ NSEventModifierFlagShift];
+        }
+    }
+    calc_keydown(characters, [theEvent modifierFlags], [theEvent keyCode]);
 }

(and the same change in [CalcView keyUp:])
and these keymap entries:

Alt 0x2dc       : 29
Alt Shift 0x2dc : 28 29
Alt 0x2c6       : 30
Alt Shift 0x2c6 : 28 30

I was able to map Alt-n to 1, Alt-N to Shift 1, Alt-i to 2, and Alt-I to Shift 2.


Note that the [NSEvent charactersByApplyingModifiers:] method, upon which this fix depends, is only available on MacOS 10.15 (Catalina) or later. The app will still run in 10.13 (High Sierra) and 10.14 (Mojave), but the fix will not work there.

MickM

unread,
Sep 25, 2023, 11:04:27 AM9/25/23
to Free42 & Plus42
Wow - thanks! It works perfectly :-). I really appreciate you doing this.
When I first looked at your fix, I wasn't clear if I subsequently needed to change all of my Alt & Alt+Shift unicode characters similarly but it turns out I don't. That leads me to think you did something special for n and i. If that's the case then I guess you should be aware that u and e and ` (the character to the left of "1" on my keyboard) are, I believe, the only other characters with this issue.

Couple other collateral concerns:

1. With my keyboard assignments in place I'm also seeing the remnants of all the default keys that weren't overwritten as still being active (i.e. "polluting" my environment). I notice that if I subsequently choose the default skin that all remnants of my keyboard mappings are flushed so I was wondering about doing the same thing when another keyboard mapping is selected. You could argue it's unnecessary, but it does lead to multiple very different keys being mapped to the same function which will lead to confusion because I have very different functions as my shifted version of the default mappings. Maybe we could have a key mapping "reset" line in the layout file. Or maybe have a MacKey line that maps an unused default key to null. Just talking out loud here...

2. Is there a way to treat the numeric pad keys separately i.e. distinguish between "+" on the numeric pad and "+" as shift = on the main keyboard? I also notice that I can't do shift 0-9 on my numeric pad (which seems like an intuitive thing to want to do). I originally had "purist" thoughts about letting shift 0-9 absolutely be their shifted counterparts on the skin, but as you well know that causes an issue with the beloved "*" for multiply. I had other ideas for dealing with that because it irked me that people who didn't have a numeric pad had the "inconvenience" of having to do shifted characters for + and * but not for - and /. This is likely my lowest priority issue and if nothing happens on this front i'm fine with that :-).

Thomas Okken

unread,
Sep 25, 2023, 5:23:00 PM9/25/23
to Free42 & Plus42
I didn't do anything special for n and i. The new code checks if the key event has no key characters, and if so, it tries -- on the assumption that the key may be a dead key -- to see which character would have been generated if the state of Shift were inverted. This assumes that dead and non-dead accents are always on the same key with the difference being the state of Shift, and that happens to be correct for n (tilde), i (circumflex), u (umlaut), e (acute accent), and ` (grave accent, special case because the non-dead version is both ` by itself and Shift Option `, at least on my MBA with U.S. keyboard and U.S. English selected).

Regarding numeric keypad keys: In the Windows version, there are separate key codes for the numeric keypad (e.g. 48 for regular 0 and 96 for numeric keypad 0); in the Linux version, there are separate KeySyms (e.g. 0 vs. KP_0); but in the Mac version, there is currently no support for this. I just checked, and there is an event modifier flag to indicate that a key is in the numeric keypad, so I could add support for that by defining a new modifier, say, NumPad, so you could create mappings like 0 vs. NumPad 0. I'll have to plug my full PC-style keyboard into my MBA to see if that works the way it seems it should... Stay tuned.

Regarding keyboard mappings "polluting" your environment: I'm not following the question, could you rephrase, or give an example of the unexpected/undesired behavior?

MickM

unread,
Sep 25, 2023, 6:22:21 PM9/25/23
to Free42 & Plus42
Many thanks for looking into the numeric pad capabilities!! 

The thing to note about my skin is that is has a vastly different layout to the default HP42S skin - something that people will have to rewire wire their brains a little to use (although it's not too hard). There are many reasons for that happening, but it is fundamentally why I have so many questions!

Anyway, regarding my comment about the default key mappings polluting my environment - if I load my skin then my keyboard mappings take precedence over the defaults, but the defaults that I didn't redefine are still in play! For example, in the default layout file "\" is R/S and "|" (i.e. "⇧\") is "PGRM". In my keyboard layout, the shifted function of "R/S" is not "PGRM" but instead "PGM.FCN". The upshot here is that if somebody notices that "\" is "R/S" (I never defined "\" so it's still in play) then for my skin they'll expect "⇧\" to be "PGM.FCN" but they'll unexpectedly get "PGRM" i.e. my environment is "polluted" with the residue of the defaults. If my skin is loaded and I subsequently load the default skin then ALL of my skin's keyboard mappings are flushed so that kind of issue can't happen with the default skin. I was hoping/expecting when my skin was loaded that the default keyboard mappings would be similarly flushed.

It sure seems that keyboard mappings are a pain in the neck to deal with (and in particular on the Mac). Looking within the default Plus42.layout file I see the following corresponding key mappings:
   GtkKey: Alt CShift      1 : 39   # DIRS.FCN
   WinKey: Alt CShift     49 : 39   # DIRS.FCN
   MacKey: Alt CShift 0x00A1 : 39   # DIRS.FCN
I'm only on the Mac, so once I get all my key mappings finalized I genuinely don't know what to do for the other platforms given that the keys are all frustratingly different (i.e. 1 or 49 or 0x00A1 all mean the same thing). I'm highly reluctant to burden you with support for that, but I'm unclear how to untangle myself from that web. I might put my skin into these forums when the time comes and perhaps your trusty forum members will come to the rescue :-).

Oh, as far as the Mac is concerned, what is CShift? I played around a little and couldn't figure it out.

Thomas Okken

unread,
Sep 25, 2023, 9:52:58 PM9/25/23
to Free42 & Plus42
The standard keymap is loaded when the app is launched, and stays in memory for the lifetime of the process. Skins can add their own mappings, including overriding the standard ones, but the standard key mappings always being available is a feature, not a bug.

Regarding CShift, that's a modifier that indicates that the calculator's Shift annunciator is on. You use it to define mappings for when Shift and the key it modifies are pressed one after the other, instead of at the same time.

Regarding the different conventions for specifying keys: that's just the way keystroke events for these three operating systems work. Maybe it would be possible to design and implement a language for specifying keyboard mappings in a way that is both layout- and OS-independent, but until someone figures that out, this is the best I can do, I'm afraid...

MickM

unread,
Sep 25, 2023, 10:52:08 PM9/25/23
to Free42 & Plus42
Thanks - I understood all that and I appreciate you explaining it all. It might be worth augmenting the header comments in keymap.txt to explain the function of CShift - I didn't recall seeing that anywhere (although it's also likely I just missed it). In the meantime, I'll add several dozen Cshift lines to my layout file...

Thomas Okken

unread,
Sep 26, 2023, 6:22:53 AM9/26/23
to Free42 & Plus42
Hmm, yes, I thought I had documented CShift, but apparently I hadn't. It's mentioned in the source code but not in any of the README or keymap files. (It is mentioned in the Free42 1.4.17 release notes, but of course I can hardly expect people to read 3000+ lines of history looking for obscure features!) I'll fix that shortly, and add the NumPad modifier in the Mac versions as well. Stay tuned...

Thomas Okken

unread,
Sep 26, 2023, 8:38:39 AM9/26/23
to Free42 & Plus42
I added the CShift documentation to the default keymap.txt, and I added the NumPad logic.
I uploaded test builds of Free42 and Plus42, to the same locations as before.

MickM

unread,
Sep 26, 2023, 9:14:46 AM9/26/23
to Free42 & Plus42
Fantastic! I won't be able to check it out until this evening and will forward my feedback then. Thanks again :-).

MickM

unread,
Sep 26, 2023, 10:33:27 PM9/26/23
to Free42 & Plus42
I was finally able to try out your new mods for NumPad. I tested out the following, and everything except the CShift lines worked properly. 
  MacKey: NumPad          0 :    162   # 0
  MacKey: NumPad   Shift  0 : 28 163   # DISP
  MacKey: NumPad  CShift  0 : 28 163   # DISP
  MacKey: NumPad          1 :    152   # 1
  MacKey: NumPad   Shift  1 : 28 153   # ASSIGN
  MacKey: NumPad  CShift  1 : 28 153   # ASSIGN

Thomas Okken

unread,
Sep 27, 2023, 3:01:19 AM9/27/23
to Free42 & Plus42
For the CShift mappings, try removing the 28s from the macros. Since the Shift annunciator is already on when a CShift mapping is triggered, the Shift in the macro turns off the Shift state, which is not what you want in this case.

MickM

unread,
Sep 29, 2023, 4:39:22 PM9/29/23
to Free42 & Plus42
I thought I was all set on the idiosyncrasies of Mac key bindings but I found (hopefully) this last one:

  MacKey:            0x0020 :    013  #   space → ENTER (this works...)
  MacKey:     CShift 0x0020 :    013  # ⇧,space → ALPHA (and this works...)
  MacKey:      Shift 0x0020 : 28 013  #  ⇧space → ALPHA (but this doesn't!)

Thomas Okken

unread,
Sep 29, 2023, 5:35:41 PM9/29/23
to Free42 & Plus42
Supporting this on the Mac would require another change in how it matches key mappings. The way it works right now is that keystrokes that produce characters with codes in the range 32-126, that is, "printable" ASCII characters, are matched without caring about the Shift key. Since the space bar produces the same printable character regardless of whether Shift is pressed or not, you can't create separate mappings for space and shift-space.

In the Windows and Linux versions, it's different, since those match virtual key codes and GTK KeySyms, respectively.

I could make this work in the Mac version, but I'll have to think about how, exactly. Something like how I'm dealing with the numeric keypad, I guess, since NumPad 0 and Shift NumPad 0 also produce the same printable character. It's doable, of course... stay tuned.

Thomas Okken

unread,
Oct 3, 2023, 6:29:26 PM10/3/23
to Free42 & Plus42
I could make this work in the Mac version, but I'll have to think about how, exactly. Something like how I'm dealing with the numeric keypad, I guess, since NumPad 0 and Shift NumPad 0 also produce the same printable character. It's doable, of course... stay tuned.

This change turned out to be really easy, and it is in the master branch of Free42 and Plus now.

MickM

unread,
Oct 6, 2023, 2:37:44 PM10/6/23
to Free42 & Plus42
I just noticed the control modifier key is now working as expected - thanks very much for doing that! :-).

Thomas Okken

unread,
Oct 6, 2023, 3:11:25 PM10/6/23
to Free42 & Plus42
I didn't change anything about how Ctrl and Alt/Option are handled... any changes you noticed in the latest test builds would have had to be caused by my changes to the way Shift is handled with the numeric keypad and Space.
Reply all
Reply to author
Forward
0 new messages