Chording Multiple Buttons

I have a four-button mouse that jfedor helped me configure so that tapping the Back button activates a drag-scroll layer but tapping either the Back button again or left mouse button will dismiss the drag-scroll layer. I was trying to figure out how to get a hold behavior on that Back button as well. Below is the drag-scroll expression:

1 recall
dup not
0x00090004 prev_input_state_binary mul
0x00090004 input_state_binary not mul
bitwise_or
0x00090001 prev_input_state_binary
0x00090001 input_state_binary not mul
not mul
1 recall
0x00090004 prev_input_state_binary mul
0x00090004 input_state_binary not mul
not mul
1 store

What I was thinking was I’d like that behavior to stay the same but if I hold 90004 and tap 90001 then it does not activate the drag-scroll layer but rather activates the left mouse button in a sticky mode, then tapping 90001 again would dismiss the sticky LMB. Unfortunately, I cannot wrap my head around these expressions and there are only a couple of examples of chording behavior that I can find. I don’t really even understand how the expression above works. Can anybody offer any direction or advice?

Thank you!

Check out this post where I have an example of using tap_state to activate/deactive a layer when a button is clicked. You should be able to modify your expression to use tap_state to engage your drag-scroll layer (I assume Layer 1), then make a second expression that uses hold_state to engage Layer 2. However, I haven’t tried that myself.

Per that whole thread, I’ve also struggled with Reverse Polish Notation. It helps a lot to use comments (which are unfortunately not saved in HID Remapper configs).

Saying all of that, I’m not sure if you really need a second layer. Why not just do this?

  • Hold 90004 = sticky left button

Then you don’t need an expression at all. You would just have to hold 90004 again to release it (instead of using 90001).

I didn’t think that I could do a hold for the sticky key toggle. How would that work? I assume I would need an expression, but anything else I assign to that 90004 button seems to break the current expression in one way or another.

That’s explained in the thread I linked to.

Sorry about that, I hadn’t had an opportunity to read it yet. I’ll check it out and post if I have any issues.

Thank you!

I’m sorry, I’m still missing something here. I tried running through the final bit of expression you posted in that other thread and I don’t understand how the final stored value works out to the behavior that you were looking for.

Below is the TAP_STATE and initial value for the register along with what your expression evaluates to. They don’t really make sense to me. Shouldn’t it evaluate to 1 any time TAP_STATE is 1, 0 otherwise? Why would it work for 0 and 0 to evaluate to 1? And 1 and 1 should evaluate to 1 because it’s still tapped, right?

I feel like I’m misunderstanding some core piece of how the expressions work but I can’t quite see it.

5 recall
5 recall /* layer active and... */
0x00090004 tap_state /* button is released after tap */
mul
not mul /* deactivate */
5 recall not /* layer not active and... */
0x00090004 tap_state /* button is released after tap */
mul
bitwise_or /* activate */
5 store

TAP_STATE 1
REGISTER 0

EVALUATES 1

==================================

TAP_STATE 1
REGISTER 1

EVALUATES 0

==================================

TAP_STATE 0
REGISTER 1

EVALUATES 1

==================================

TAP_STATE 0
REGISTER 0

EVALUATES 1

I think the key is how bitwise_or works. From Google Gemini:

A bitwise OR is a binary operation that compares two numbers bit by bit, returning a 1 in each bit position if the corresponding bits in either or both input numbers are 1, and 0 only if both bits are 0.

This means that:

0,0 bitwise_or = 0
0,1 bitwise_or = 1
1,0 bitwise_or = 1
0,0 bitwise_or = 1

I’m still pretty new at this stuff, but I’m going to take a shot at it. @jfedor will hopefully correct me if I lead you in the wrong direction.

If the layer is not active (Register 5 = 0, tap_state = 1):

5 recall (Stack = 0)
5 recall (Stack = 0,0)
0x00090004 tap_state (Stack = 0,0,1)
mul (Stack = 0,0)
not (Stack = 0,1)
mul (Stack = 0)
5 recall (Stack = 0,0)
not (Stack = 0,1)
0x00090004 tap_state (Stack = 0,1,1)
mul (Stack = 0,1)
bitwise_or (Stack = 1)
5 store (Result stored = 1)

The value of Register 5 changes from 0 to 1, which triggers the mapping and activates the layer.

If the layer is already active (Register 5 = 1, tap_state = 1):

5 recall (Stack = 1)
5 recall (Stack = 1,1)
0x00090004 tap_state (Stack = 1,1,1)
mul (Stack = 1,1)
not (Stack = 1,0)
mul (Stack = 0)
5 recall (Stack = 0,1)
not (Stack = 0,0)
0x00090004 tap_state (Stack = 0,0,1)
mul (Stack = 0,0)
bitwise_or (Stack = 0)
5 store (Result stored = 0)

The value of Register 5 changes from 1 to 0, which also triggers the mapping and deactivates the layer.

But that’s not all. The expression also runs when tap_state returns to its 0 state.

If the layer is not active (Register 5 = 0, tap_state = 0):

5 recall (Stack = 0)
5 recall (Stack = 0,0)
0x00090004 tap_state (Stack = 0,0,0)
mul (Stack = 0,0)
not (Stack = 0,1)
mul (Stack = 0)
5 recall (Stack = 0,0)
not (Stack = 0,1)
0x00090004 tap_state (Stack = 0,1,0)
mul (Stack = 0,0)
bitwise_or (Stack = 0)
5 store (Result stored = 0)

If the layer is active (Register 5 = 1, tap_state = 0):

5 recall (Stack = 1)
5 recall (Stack = 1,1)
0x00090004 tap_state (Stack = 1,1,0)
mul (Stack = 1,0)
not (Stack = 1,1)
mul (Stack = 1)
5 recall (Stack = 1,1)
not (Stack = 1,0)
0x00090004 tap_state (Stack = 1,0,0)
mul (Stack = 1,0)
bitwise_or (Stack = 1)
5 store (Result stored = 1)

In both cases, the expression results in no change to the value already stored in Register 5. So, the mapping is not triggered.