hid-remapper icon indicating copy to clipboard operation
hid-remapper copied to clipboard

Expressions - ability to call one expression from another

Open KillyMXI opened this issue 10 months ago • 8 comments

To keep them more DRY, modular, readable.

Let's look at the extended expression from https://github.com/jfedor2/hid-remapper/issues/131#issuecomment-2186958622 It contains quite mouthful piece repeated twice, that does something meaningful and can be extracted as a function in other programming languages. Would be great to be able to do a similar thing.

New expressions may look like this:

Expression 1:

/* 1 on A press, otherwise 0 */
0x00070004 input_state_binary
0x00070004 prev_input_state_binary not
bitwise_and

Expression 2:

1 recall

/* A pressed and layer not active... */
1 expression
1 recall not
bitwise_and
/* -> activate it */
bitwise_or

/* A pressed and layer active... */
1 expression
1 recall
bitwise_and
/* -> deactivate it */
not bitwise_and

/* B pressed... */
0x00070005 input_state_binary
/* -> deactivate layer */
not bitwise_and

1 store

Parameters can be passed through registry:

Expression 1:

/* 1 on input press, otherwise 0 */
3 recall input_state_binary
3 recall prev_input_state_binary not
bitwise_and

Expression 2:

1 recall

/* A pressed and layer not active... */
0x00070004 3 store 1 expression
1 recall not
bitwise_and
/* -> activate it */
bitwise_or

/* A pressed and layer active... */
0x00070004 3 store 1 expression
1 recall
bitwise_and
/* -> deactivate it */
not bitwise_and

/* B pressed... */
0x00070005 input_state_binary
/* -> deactivate layer */
not bitwise_and

1 store

KillyMXI avatar Jan 25 '25 11:01 KillyMXI

I'm still figuring how expressions work.

Seems like at least trivial cases are already possible like this:

Expression 1:

/* 1 on A press, otherwise 0 */
0x00070004 input_state_binary
0x00070004 prev_input_state_binary not
bitwise_and
3 store

Expression 2:

1 recall

/* A pressed and layer not active... */
3 recall
1 recall not
bitwise_and
/* -> activate it */
bitwise_or

/* A pressed and layer active... */
3 recall
1 recall
bitwise_and
/* -> deactivate it */
not bitwise_and

/* B pressed... */
0x00070005 input_state_binary
/* -> deactivate layer */
not bitwise_and

1 store

Documentation on expressions lifecycle in context of a mapping and interaction between expressions might be handy to have.

KillyMXI avatar Jan 25 '25 12:01 KillyMXI

hey, i just finished making a tool to simulate hid remapper expression, personally i think this tool might be useful for debugging, creating any complex expression you want, and or might be helpful for figuring out the language too

HID remapper expression evaluator

hope this helps

onixldlc avatar Feb 03 '25 09:02 onixldlc

Hey @onixldlc that's nice and handy. I also thought about making something around the expression language. But specifically for my issues, I think it is not helpful.

I think I have a grasp of the stack language itself. What I have trouble with and what lacks documentation is the lifecycle of the remapper itself, where in the loop multiple mappings, expressions, etc are evaluated...

KillyMXI avatar Feb 03 '25 09:02 KillyMXI

@onixldlc Oh, that is exciting!

@KillyMXI I hope to improve the documentation in the future, it roughly works like this every frame.

First, tap/hold/sticky mappings are processed. Then layer-activating mappings are processed. Then expressions are evaluated one after another. Every expression is executed once, in order, regardless of whether its output is used in a mapping. Then regular mappings are processed. The order in which they're defined doesn't matter.

This means that for example if you're using expressions to set layer state, you're getting a 1 frame delay, because the results of the expression will be used on the next frame. The delay itself shouldn't realistically be an issue as it's 1 millisecond, just something to keep in mind as it can affect the logic.

jfedor2 avatar Feb 03 '25 14:02 jfedor2

@onixldlc , excellent, but...

I was about to post a question about the clamp expression and wanted to make sure I understood its x,y,z inputs.
Your hid remapper expression evaluator answered it for me. It was as I expected with the lifo nature of the stack; First item pushed is x, next is y, last is z.

I entered the following expression as a test: 124 4 add 8 sub 127 -127 clamp and got the result of 127 as expected.

Then I added 1 store:

124 4 add 8 sub 127 -127 clamp 1 store

I thought it would push 1, then pop 1 and pop 127 and store 127 in register 1, but it gave me an error: Error The expression reduce to an empty result.

I can't see the contents of R1 in my browser (Safari), so I selected, copied and pasted and R1 does contain 127, but I'm not clear on the error.

Am I missing something?

MackNNations avatar Feb 18 '25 20:02 MackNNations

It is okay for an expression to finish with an empty stack if you're not using the expression itself in your mappings, but registers that it set.

jfedor2 avatar Feb 18 '25 21:02 jfedor2

Oh woops, my bad, i should really make the website scale based on the user screen, i'll probably fix that later.

And for the error, yeah most of the example expression that i used always rely on the output of the expression itself, like the socd and the proportional mouse movement via arrow, and so using register as a method to control your input never really crosses my mind.

But i guess for the sake of clarity i might change that empty expression error into a warning or info instead, so that it won't confuse anyone else

onixldlc avatar Feb 18 '25 22:02 onixldlc

Thanks for the clarifications.

MackNNations avatar Feb 19 '25 00:02 MackNNations