evsieve icon indicating copy to clipboard operation
evsieve copied to clipboard

long / short press

Open muff11n opened this issue 1 year ago • 2 comments

is there a way to do a long press different output than short press, for example:

lets say theoretical parameter hold for 0.5 second would give me b and holding it for 1 second would give me c

evsieve --input /dev/input/by-path/* grab --hook key:a send-key=key:b hold=0.5 --hook key:a send-key=key:c hold=1 --withhold key:a --output

btw the tool is great, it doesn't affect other remaps, so thank you

muff11n avatar Sep 29 '24 06:09 muff11n

A somewhat convoluted way to get this done with the current version of evsieve would be the following script:

evsieve --input /dev/input/by-id/YOUR_KEYBOARD domain=in grab \
        --map  key:a:0@in key:info:1@a-is-released key:info:0@a-is-released key:a:0@in \
        --hook key:a@in key:info@a-is-released period=0.5 send-key=key:a@out  \
        --withhold key:a@in \
        --hook key:a@in key:info@a-is-released period=0.5 send-key=key:b \
        --withhold key:a@in \
        --hook key:a@in key:info@a-is-released send-key=key:c \
        --withhold key:a@in \
        --block key:info@a-is-released \
        --output

I don't think there is a nonconvoluted way to get this done right now. That said, a clause for --hook to only trigger if its keys are held for a certain minimum/maximum amount of time is potentially a good idea though. I'll consider implementing it after I am done stabilizing version 1.5.0.

To explain the convoluted script:

The first map generates an arbitrary token keypress (key:info@a-is-released) whenever you release the A key, and puts that keypress into the event stream before the key:a:0 event.

The next hooks make use of the period=0.5 clause, which causes the hook to only activate if the keypress key:info@a-is-released is observed within 0.5 seconds after seeing the key:a:1@in event.

Each of the --withhold arguments causes the key:a:1@in event to be dropped if the preceding hook activates, or causes the key:a:1@in event to be delayed by 0.5 seconds if the corresponding key:info:1@a-is-released event is not observed within that timeframe.

This means that the key:a:1@in event will wait 0.5 seconds on the first hook and withhold, then advance to the second hook and withhold and wait another 0.5 seconds, and finally move on the the third hook&withhold and wait there indefinitely until either key:a is released or a key:info:1@a-is-released event arrives.

Finally, the first hook uses send-key=key:a@out to send the key:a event to the output device if the user presses the A key for less than 0.5 seconds. In order to not confuse this key intended for the output device with a key from the input device, all other key:a events have been specified as key:a@in.

KarsMulder avatar Sep 29 '24 18:09 KarsMulder

I knew it was possible with some voodoo :), thanks, but the short version in the future would be nicer, thanks anyways

muff11n avatar Sep 30 '24 17:09 muff11n

I've created a variation of @KarsMulder's script which triggers without needing to release the key, and thought I should share. In this example, if you hold a, the b key will be sent as soon as 1 second as elapsed. If you hold a for less than 1 second, nothing will happen.

--map key:a:0 key:info:1@released key:info:0@released key:info:0@repeat
--map key:a:2 key:info:1@repeat
--hook key:info@repeat key:info@released period=1
--withhold key:info@repeat
--copy key:info:1@repeat key:info:0@repeat
--toggle key:info@repeat @repeat @null id=hold-a
--hook key:info@repeat toggle=hold-a:2
--hook key:info@released toggle=hold-a:1
--hook key:info@repeat send-key=key:b
--withhold key:info@repeat
--block key:a key:info@repeat key:info@released @null

There's even more going on here than the original, so let me try to break it down line by line:

  • 1: An a key press event is mapped to a key press and key release of key:info@released, same as the original. Additionally, a key released for key:info@repeat is sent. This is to make sure that the proceeding hook is reset, in case a was previously held for less than 1 second.
  • 2: An a key repeat event is mapped to a key press (but not a key release!) of key:info@repeat. It's important not to have a key release here, since a key release event would prevent the hook from ever firing.
  • 3 & 4: A hook based on key:info@repeat and key:info@released prevents the events stream from continuing until the period has elapsed, similar to the original.
  • 5: If we've made it past the hook, we can safely turn the "held" key:info event into a proper key-press and key-release pair, so it can trigger the proceeding hooks.
  • 6: We need a way to make sure that the output doesn't fire on each @repeat event, so we can use a toggle which will let the first @repeat event through, but discard the remaining @repeat events. By default, events are kept in the @repeat stream, but they can be toggled into the @null stream which are later discarded.
  • 7: When key:info@repeat event is received, we switch the toggle to the @null stream, so any future key:info@repeats will not be let through. The current key:info@repeat continues through the stream, however.
  • 8: Whenever the @released events are eventually received, we know it is safe to reset the toggle back to @repeat rather than @null. This way, future key:info@repeat events are able to proceed through the stream as normal.
  • 9 & 10: If we've reached this point and have a key:info@repeat event, then it must be the first key:info@repeat which was held back for 1 second. We can now send the output event (the b key).
  • 11: Block undesired keys from the output.

The basic idea is similar to the original concept, but there's some extra complexity around managing the key release event for key:info@repeat and making sure that we only trigger the output once per key hold. Also note, the actual time it will take for the event to trigger will be a bit longer than the specified period, since the timer won't start until the key repeat events start.

132ikl avatar Jan 12 '26 19:01 132ikl