Key id symbols wanted

…meanwhile in another thread: Avoid macro duplication for numbers row .

(The suggested feature is not even implemented and someone is already making that exact mistake.)

Edit: or maybe not. Not sure what his original idea was. I guess he actually wanted to use keyids as a source of the difference…

But yeah, I do agree that my proposal is still quite ugly aesthetically… not sure what the best way is.

The current state of affairs has the benefit that the numeric ids can be saved into registers, which I am actually using in some macros. I am not sure how to preserve this property (alongside with backward compatibility) with your proposal (because of clashing numbers with number keys), on the other hand admittedly I am not sure that it is actually a problem.

I’d really like to see key symbols read as nicely as keyabbrevs, which is only possible by breaking backward compatibility because of the number keys.

An alternative notation could be used for key id numbers, such as simply prefixing them with $.

As for saving keys to registers, when specifying a key symbol, its numeric key id could be saved into the register.

I’ve played a bit trying to “autodetect” the host keymap. Here is what I have so far:

C:\dev\uhk-learn-layout>python uhk-learn-layout.py --help
usage: uhk-learn-layout.py [-h] [--debug] [--generate-macro] [--use-altgr] [--macrodelay] [--print-canonical]
                           [--canonical] [--showmap] [--compact] [--input INPUTFILE]

determine the host layout from the output of the "learn_layout" macro run on a UHK

options:
  -h, --help         show this help message and exit
  --debug            show debugging information for developers
  --generate-macro   generate learn_hostmap macro for UHK Agent. Paste the generated macro code into a
                     macro command, and bind it to a key. Execute the macro (by tapping the key), and feed
                     the output into this script to learn the host keymap.
  --use-altgr        use AltGr combinations in macro (in addition to standard keypress and shift-keypress)
  --macrodelay       insert delays into the macro (so it runs a bit slower)
  --print-canonical  print the canoncial string(s) for this keymap
  --canonical        show the canonical name for this keymap
  --showmap          show the keyboard layout
  --compact          show a compact version of the keyboard layout
  --input INPUTFILE  read input from this file

Generate the macro:

C:\dev\uhk-learn-layout>python uhk-learn-layout.py --generate-macro > macro.txt

C:\dev\uhk-learn-layout>more macro.txt
tapKeySeq keypadPlus keypadPlus keypadPlus keypadPlus keypadPlus space B E G I N space keypadPlus keypadPlus keypadPlus keypadPlus keypadPlus space space enter
tapKeySeq keypadMinus keypadMinus 6 4 keypadMinus graveAccentAndTilde space space enter
tapKeySeq keypadMinus keypadPlus 6 4 keypadMinus LS-graveAccentAndTilde space space enter
tapKeySeq keypadMinus keypadMinus 6 5 keypadMinus 1 space space enter
tapKeySeq keypadMinus keypadPlus 6 5 keypadMinus LS-1 space space enter
tapKeySeq keypadMinus keypadMinus 6 6 keypadMinus 2 space space enter
tapKeySeq keypadMinus keypadPlus 6 6 keypadMinus LS-2 space space enter
...

Using Agent, paste the generated text into a macro (as a macro command), then bind it to a key. I assigned it to Fn-=.

Next, run the program again on the host, then press Fn-= so the output of the macro is sent into uhk-learn-layout.py:

C:\dev\uhk-learn-layout>python uhk-learn-layout.py --canonical --showmap --compact
+++++ BFDUK +++++
... (lots of output) ...
----- FKS -----
Canonical keymap name: US-Colemak
R1:  [ `~ ]  [ 1! ]  [ 2@ ]  [ 3# ]  [ 4$ ]  [ 5% ]  [ 6^ ]  [ 7& ]  [ 8* ]  [ 9( ]  [ 0) ]  [ -_ ]  [ =+ ]
R2:          [ qQ ]  [ wW ]  [ fF ]  [ pP ]  [ gG ]  [ jJ ]  [ lL ]  [ uU ]  [ yY ]  [ ;: ]  [ [{ ]  [ ]} ]  [ \| ]
R3:          [ aA ]  [ rR ]  [ sS ]  [ tT ]  [ dD ]  [ hH ]  [ nN ]  [ eE ]  [ iI ]  [ oO ]  [ '" ]
R4:  [ -_ ]  [ zZ ]  [ xX ]  [ cC ]  [ vV ]  [ bB ]  [ kK ]  [ mM ]  [ ,< ]  [ .> ]  [ /? ]

Now, changing the host keymap (but nothing on the UHK), and run again:

C:\dev\uhk-learn-layout>python uhk-learn-layout.py --canonical --showmap --compact
+++++ BEGIN +++++
... (lots of output) ...
----- END -----
Canonical keymap name: DE
R1:  [ ^° ]  [ 1! ]  [ 2" ]  [ 3§ ]  [ 4$ ]  [ 5% ]  [ 6& ]  [ 7/ ]  [ 8( ]  [ 9) ]  [ 0= ]  [ ß? ]  [ ´` ]
R2:          [ qQ ]  [ wW ]  [ eE ]  [ rR ]  [ tT ]  [ zZ ]  [ uU ]  [ iI ]  [ oO ]  [ pP ]  [ üÜ ]  [ +* ]  [ #' ]
R3:          [ aA ]  [ sS ]  [ dD ]  [ fF ]  [ gG ]  [ hH ]  [ jJ ]  [ kK ]  [ lL ]  [ öÖ ]  [ äÄ ]
R4:  [ <> ]  [ yY ]  [ xX ]  [ cC ]  [ vV ]  [ bB ]  [ nN ]  [ mM ]  [ ,; ]  [ .: ]  [ -_ ]```

Now, let’s do this better, and add AltGr functionality. Re-generate the macro:

C:\dev\uhk-learn-layout>python uhk-learn-layout.py --generate-macro --use-altgr > macro.txt

Again, paste it into Agent and save the updated macro to the UHK. Now run the detection again (run the script, then press Fn-=):

C:\dev\uhk-learn-layout>python uhk-learn-layout.py --canonical --showmap --compact
... (lots of output) ...
Canonical keymap name: US-Colemak-International
R1:  [ `~~ ]  [ 1!¡ ]  [ 2@º ]  [ 3#ª ]  [ 4$¢ ]  [ 5%€ ]  [ 6^ħ ]  [ 7&ð ]  [ 8*þ ]  [ 9(‘ ]  [ 0)’ ]  [ -_– ]  [ =+× ]
R2:           [ qQä ]  [ wWå ]  [ fFã ]  [ pPø ]  [ gG˛ ]  [ jJđ ]  [ lLł ]  [ uUú ]  [ yYü ]  [ ;:ö ]  [ [{« ]  [ ]}» ]  [ \|* ]
R3:           [ aAá ]  [ rR` ]  [ sSß ]  [ tT´ ]  [ dD¨ ]  [ hHˇ ]  [ nNñ ]  [ eEé ]  [ iIí ]  [ oOó ]  [ '"õ ]
R4:  [ -_– ]  [ zZæ ]  [ xX^ ]  [ cCç ]  [ vVœ ]  [ bB˘ ]  [ kK˚ ]  [ mM¯ ]  [ ,<¸ ]  [ .>˙ ]  [ /?¿ ]

Or with other keymaps:

Canonical keymap name: DE-International
R1:  [ ^°  ]  [ 1!  ]  [ 2"² ]  [ 3§³ ]  [ 4$  ]  [ 5%  ]  [ 6&  ]  [ 7/{ ]  [ 8([ ]  [ 9)] ]  [ 0=} ]  [ ß?\ ]  [ ´`  ]
R2:           [ qQ@ ]  [ wW  ]  [ eE€ ]  [ rR  ]  [ tT  ]  [ zZ  ]  [ uU  ]  [ iI  ]  [ oO  ]  [ pP  ]  [ üÜ  ]  [ +*~ ]  [ #'  ]
R3:           [ aA  ]  [ sS  ]  [ dD  ]  [ fF  ]  [ gG  ]  [ hH  ]  [ jJ  ]  [ kK  ]  [ lL  ]  [ öÖ  ]  [ äÄ  ]
R4:  [ <>| ]  [ yY  ]  [ xX  ]  [ cC  ]  [ vV  ]  [ bB  ]  [ nN  ]  [ mMµ ]  [ ,;  ]  [ .:  ]  [ -_  ]
Canonical keymap name: UK-International
R1:  [ `¬¦ ]  [ 1!  ]  [ 2"  ]  [ 3£  ]  [ 4$€ ]  [ 5%  ]  [ 6^  ]  [ 7&  ]  [ 8*  ]  [ 9(  ]  [ 0)  ]  [ -_  ]  [ =+  ]
R2:           [ qQ  ]  [ wW  ]  [ eEé ]  [ rR  ]  [ tT  ]  [ yY  ]  [ uUú ]  [ iIí ]  [ oOó ]  [ pP  ]  [ [{  ]  [ ]}  ]  [ #~\ ]
R3:           [ aAá ]  [ sS  ]  [ dD  ]  [ fF  ]  [ gG  ]  [ hH  ]  [ jJ  ]  [ kK  ]  [ lL  ]  [ ;:  ]  [ '@  ]
R4:  [ \|  ]  [ zZ  ]  [ xX  ]  [ cC  ]  [ vV  ]  [ bB  ]  [ nN  ]  [ mM  ]  [ ,<  ]  [ .>  ]  [ /?  ]

So, this approach works to learn how the host generates characters for each keypress on the UHK. I have (in the python code) now a mapping table that maps keyids to the character generated on the host for the key itself, for shifted, and for AltGr’d versions of the key.

I would like to be able to install such a mapping table into the UHK, so that when I send text from a macro, I can let the keyboard convert the string to produce that string on the host, basically reverse mapping each character to which keyid (potentially with Shift or AltGr modifiers) it needs to activate for that character.

That would make some macros so much more usable, because at the moment, this is what I have to do:

Clearly everyone can see at a glance that this string really means “http://ultimatehackingkeyboard.com:slightly_smiling_face: :upside_down_face: :slightly_smiling_face: :wink:

You mean scancode.

(Each post has to have at least 20 characters, so here you are!)

Also, @maexxx, this is an entirely different problem than what this thread is about…

Would you be able to provide a cross-platform proof-of-concept implementation inside Agent?

No, I mean key id (that internal id that the UHK uses to reference each key). The macro emits series of lines which send out the key id of a key, information whether it is going to be activated with or without Shift and/or AltGr, and then it will tap the key followed by two space characters (to make sure dead keys send something). It walks the UHK keyboard from top left to bottom right. The script collects all that information and builds a mapping table for each modifier state, mapping key id to the received character.

This is a section of the macro for the first two keys, unshifted and shifted:

tapKeySeq keypadMinus keypadMinus 6 4 keypadMinus graveAccentAndTilde space space enter
tapKeySeq keypadMinus keypadPlus 6 4 keypadMinus LS-graveAccentAndTilde space space enter
tapKeySeq keypadMinus keypadMinus 6 5 keypadMinus 1 space space enter
tapKeySeq keypadMinus keypadPlus 6 5 keypadMinus LS-1 space space enter

Key id 64 for the graveAccentAndTilde key and key id 65 for number 1 key.

Ideally, you would not have to paste the macro code generated by the script, but the script could just tell the UHK to execute those commands.

I thought about doing something in Agent, but unfortunately I do not speak Javascript, nor am I too familiar with the technologies used to build Agent. I think that is a too high hurdle for me at this time. That’s why I built an external tool only as a proof of concept.

Ah, I see.

I have just now actually read the post. Originally I thought you were querying system scancode ↔ character mapping.

I take back the nudge to provide a workable prototype. No offense, but at the moment, from a practical point of view, I consider it a more-or-less insane approach.

No, because that’s heavily system dependent. I was trying to find an approach that solely works through a combination of UHK firmware and Agent (or another tool on the host side instead of Agent).

The whole idea got triggered by the discussion about key id vs. key name. The key ids are just the hardware position of a key. The key name is the label that is assigned to it in a US layout. Both key id and key name have a 1:1 fixed relationship, and they also relate 1:1 to scancodes.

But then you also have the host character that is produced by the host OS. That’s a concept that currently is not anywhere in Agent or the UHK firmware. I believe it should be introduced.

It would help all those users that use non-US keyboard layouts – for example those that buy a German labeled UHK, and then get confused when Agent shows them a US keyboard, and when they want to send a / character, they have to use & (because that is the US symbol on Shift-7 where their German keycaps have the / symbol). Agent doesn’t even show them the labels they have on their actual keycaps. I think the UHK software can do better than that.

Let’s assume we can somehow find out the key id to host character mapping, and load that into an in-memory table for the firmware to use. Maybe even with some macro commands:

set hostmap.<character> <keyid> <mods>
(or the other way around)

Then - as a starting point - I would introduce additional tapKey-like function, something like:

tapHostKey y

which would consult the hostmap first to convert character y into the key id it needs to tap together with any modifiers (Shift, AltGr).

Send text commands would also allow to send hostmap-converted strings etc.

Maybe this is the starting point for a discussion how to get this done (in stages?).

@maexxx It’s truly remarkable how much thought you put into this. Please start a dedicated topic and reference the repo of your tool for further discussion.

The whole idea got triggered by the discussion about key id vs. key name. The key ids are just the hardware position of a key. The key name is the label that is assigned to it in a US layout. Both key id and key name have a 1:1 fixed relationship,

No. For instance key name space (according to its en us print) corresponds to two different key ids.

and they also relate 1:1 to scancodes.

This is also clearly not true. If I map the same scancode onto every key of the keyboard, then all keids relate to the same scan code.

What am a i misunderstanding now?

Maybe I am misunderstanding something. You are of course correct that you can map different scancodes to one or many keys, so it’s not a 1:1 mapping. But I am not talking about the configuration part of Agent (where you assign something to your keys).

Forget about Agent for now. Let’s talk firmware only.

tapKey r will tap exactly 1 key, so it’s name r maps 1:1 to the key id of the key that carries the legend r on the US keymap. So that’s why I am referring to this as a 1:1 mapping. That’s what I mean with fixed relationship.

I’d like to extend this, so that tapHostKey r will consult mapping tables so that it picks that key which will produce the r character on the host.

As @mlac suggested, I will open a new topic for this discussion. See you there!

It is a very fine line between genius and insanity. I enjoy walking along this line. :wink:

2 Likes

@mlac The trouble is that in order to preserve composability of KEYID functionality, it is necessary to preserve numeric compatibility of KEYIDs, which means that whatever name set you come up with, it has to be unambiguous w.r.t. NUMBER tokens.

(This is not a problem for ifGesture, but there are other commands which use KEYIDs.)

@kareltucek Please provide some examples which would hurt composability.

activateKeyPostponed [atLayer LAYERID] KEYID
set keymapAction.LAYERID.KEYID ACTION
{ifKeyActive | ifNotKeyActive} KEYID
{ifKeyDefined | ifNotKeyDefined} KEYID
{ifKeyPendingAt | ifNotKeyPendingAt} <idx in buffer (NUMBER)> KEYID

For all these commands it makes sense to substitute value either remembered in a register, or pulled via the % operator from postponing queue.

@kareltucek Please provide even more concrete examples that can be fed to the macro engine.

I don’t have one in mind and don’t see a reason to have to justify above point further.

If you mean really example and not usecase, then consider something like this:

setReg 1 89
ifKeyActive #1 tapKey b