From 6b4f9f12ffc42ca84db032a7f6ff0af5682009e9 Mon Sep 17 00:00:00 2001 From: Raheman Vaiya Date: Fri, 28 Feb 2025 18:30:15 -0500 Subject: [PATCH] timeout: Add new behaviour when used as a tap target (#879, #738, #944) This patch expands the semantics of timeout() to cover the case in which it is used as a tap target. This facilitates a number of novel use cases, like discriminating between single/double tap or implementing per-key oneshot timeouts. Specifically timeout() is now defined in terms of arbitrary key events rather than the behaviour of the key to which it is bound (if any). The new definition executes the second action if no key events occur before the timeout expires. The implication of this is that when timeout() is executed as the result of a tap action, action2 will be executed after the timeout expires unless another key is struck in the interval. Note that this is backward compatible with the old definition, since a key up event (i.e a tap) will result in a resolution to the first action if timeout() is directly bound to a key. For Example: tab = overload(control, timeout(a, 100, b)) will presently produce no effect when 'tab' is tapped. Under the expanded definition, tapping tab will produce 'b' if 100 milliseconds elapse without an interceding key event. --- data/keyd-application-mapper.1.gz | Bin 1643 -> 1836 bytes data/keyd.1.gz | Bin 10015 -> 10094 bytes docs/keyd.scdoc | 14 +-- src/keyboard.c | 142 +++++++++++++++++------------- src/keyboard.h | 28 +++--- t/double-tap.t | 24 +++++ t/test.conf | 11 ++- t/timeout-nested-1.t | 20 +++++ t/timeout-nested-2.t | 31 +++++++ t/timeout-overloadt.t | 21 +++++ t/timeout1.t | 9 ++ t/timeout2.t | 1 + t/timeout3.t | 19 ++++ 13 files changed, 241 insertions(+), 79 deletions(-) create mode 100644 t/double-tap.t create mode 100644 t/timeout-nested-1.t create mode 100644 t/timeout-nested-2.t create mode 100644 t/timeout-overloadt.t create mode 100644 t/timeout3.t diff --git a/data/keyd-application-mapper.1.gz b/data/keyd-application-mapper.1.gz index 850638377c2e0a94a797dcb5b88885f38e253a76..18212e7bd8843bcaf1001ed6dfdb1a5123e8e161 100644 GIT binary patch literal 1836 zcmV+{2h;c;iwFP!000001C>{6Z`(!|{apTvsTW9WLl4{C?uQ^7Ku%OwXyO=_({5lH zixD}b1|D+QnV}RFL4SMCy)%?$XI)^81S02o?m73x(c(Z{s75&%R3VmI;q$`g;&gO+ zIy%cnn0;&O)~KKa#=BZIK?ZFbQCcU$N_){dTRB+^JT^86xt7|m^?Bwjn;mHrfle2?tbd`up8K%cV6Tx*;LB+r_DcKe!QB`=JdWO&!0JTQC^E%Ob-h@eWizC zAysV~{fENHU7TF+CfCX>t>>x!qK{WMvyYekJ;U`t>@Ukk*K_gD>^G+&RT>ty3vCxh zix=XCq|!}pxPzbbvcxWAqu7wVi7jUMai^41`$1Z@&hIC0G!iKtl3TSEAHL z5hkMKddfT$#KS(9iHUdp&_HaMln2<0a`xvK$HYU&B-t4EW5n2sl+uk43hqY$Lt&Z1 z{!WtO-E{u;dd7Ot@B~rgbPLQYov+|M5`O&rB{V{}5I5{8P=svs9lsRW5~ed5%XhisXfDEMtP(D+kq@doo5z!1GX+BADY7{L{pq!wTQ3T$eh> zCH~$gghgfv2f?h2T}mTdXB0U~3Hb|>2?mc<$fKpl4|XOnH;+{mD;cdWtub~(wm|y& z71P|5NyP7NUt=+x?9E-4efL^@X`S+(P@cYd{C zGJH+at)T6X14t;@{%!Fvw*;aLWVp{h`ngVb`q>!{dkTrvh`e z%$+@)HC`70)XJRrKcdC?%Gp|XHEkf^&KlnIKlkur_5IQR4=p?j9-=o^Lr^jqo)Ux5 zme^v(B3(%M{p|tIv1mTcSurp;w=b!n`#3?xA`EHQA*J>TM-0z?{8w&`bw}*!NLOo` z5)sk!UdPD}ZfBRIYV2olPfH0eNt}v+j&YxQmu!P{;DdD3Tren_guzdXZ1P{iGJAA_ zpc8gQ2oArEsW+oqzzb1XV%|##0FBM1vv3mt8!;@!-_|Aa#R=pvR>p9Gtq`E9kl|wU zrZ~`}G_fQ}@o9d2D!i@`cFCD1fmhU)6UToBV!pv)MCJuWNep@vBkNKNkUEgr<@{#y z{{5t{d(b=}mSfrAoa8mdDg`7mEk&N|nq7FIO4TXLJllFv$YmZNHk`JRAHbv&h4!tH zWT2xmgpdsy>Fp& zQ%?hJod>PDRxu~($PHLU21B<>mu4}8f&?_feqSY{OOZB+{3$@nZFY_zgnS$x@9>umGF__=7 zBZZmdJ}deyTE`kt*=}k-DCk)Kr7BOBff^pR?5lD~p9!@2fwh1nxXY|Xr>JQ} za5s7H?eo-eQeQCG1N65}o6fflGa0R8>aySv0C;3OccH=l+XwUB0Te~5Lf5gM`#s7D ahSw2hCb^wQN>b*bCHM>0TuuTz4*&qbeTwz~ literal 1643 zcmV-x29)_9iwFP!000001C>@`Z`(Ey{XG4OTLyGV5X()_tw7Nga9S@j)}}?AZY$zo zpe0HsLWv$pIrT8?x9{Ciij!>_upmJsioARG?(XsO#U7pMR(ln7Ny{yTqI3lv=f}tS zNtWa5YuBu;j+(IEHM)%|8rM?gJjIm>wDzv{sv$hKE|S_PW7X1XQh^kmoqr_LR*u%H z)>&>eE!zFOro0R^`HdF)>bES{meNnZ`gNAKt1MR~S%Gk>YtvGF2eWW(1dtb5e)$9K z-RNyOQtP!fg+LoMc+q~3_Kxv)^7Q1{=;`y($@9G||B&VLALxAYZpzP(9HbF>jYq=w zK({O1(yeJrcZ-;dE>N*jZLPy#ZT{)}{fGH%&hLxr^bwVmHDc~s)*dN})LI)8mJ z`*1mXf6hx2u+aJSZzruD^TatLx0V&ZGI(aFx zC19JjPC@7mLQ_L1w6f2xCu1~I`LbaUl_J&+VS^-|r zg;av%Iq1kA?gdU^tqW6`0#3qB=n)Hb?DWU1!-BFZJiO*C8;< z<5v&gPw>8YlBeG+uM8|mwfwx&UNgAC5>wC4U8_0te_{v;gaEjDnHYwrSJ(95=PP>0 zS>WdDlOtOn9QosAG!en}$L}3Ud$)*A^LGK-^f@~+osM@in8 z7`I%H%0?91HGM~~M^P<@g)3Q@-(QNu5>8!(I6B-NKPG%@RN>wJtPQ3FptUNP{{!A! zth{SX*YE)b?wyrG|9VC?>n{)fKeYIuWtiTirJ!UoI%WouTV_ibOLTp`crtj8cutc0 zccn@_5mzdrG;ke+^a1-p1E1R0Ai0K#(aAU87S1|RM8^}4tsr9(F8oecL7vJuf`NybGjg5fUWR)O+yPYj_(QRAlR`~T4^m75_80C6ia ziq74-&Qoc^+A0pv^R<9xiUz{;dvXnWaDD996c#`8CSPzVIiKCFh9qzUowJG^NW=qF z;oyj};r(&AY&*?2kaXh%Xf=&aYGD#LU=`YeDTJM7N!vgIhMaIeOV(7n*cMjzY|mPN zE)nV7_=I-2b^hjvM0=YqaDCrlmJ)@z4g{r-E44u)i$VvHN$B%t>l!$M*U`g*VmSb?bwnLAV^I=ReIZ-k-mlo?p^WlZ)Bp^zC#QZ+@J9 zdh_d>>FLKaPH0lHH+tFC(i;{-1{Wi9`xG0F@L!s~Z>@eLKyU5i!dIP*FX-uTsfcL6 z{eG2)|Xw85WWUy$ah9bpxmpH;)6{|B^IURoVv%*3>I@x75-lCb+(Z z&>(A6qS$vZ=94I{`4qYvtsOHY1_+E2tr=WHhP#^YCYpVCjqf~qn$XZb=HQi#QTT*_ zTRFwtgluV5i~Qe%KSsb~34MGh6(tLutQ;JXi4aXLZIHKt3bQa~PE-Q7Yurk$swJ$; z;T>xc11gW>!hP9k9^%m4Z822)yq^I1#y+|S=s%0Lx9?hEGDRoUWznMn@F;QKwT9I1 p(9HJ?a2M$s^T2>mvIwEa@+QH|l#JJqByD$>^jZ?000W!GX($u diff --git a/data/keyd.1.gz b/data/keyd.1.gz index c6e670234f557eb94745db4a6406525110569794..51161bb5811a69681b3132f857b699e9a484091d 100644 GIT binary patch literal 10094 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 Qt=I_E&SF~=SlDgd? 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 diff --git a/docs/keyd.scdoc b/docs/keyd.scdoc index 553f370..b972c57 100644 --- a/docs/keyd.scdoc +++ b/docs/keyd.scdoc @@ -738,16 +738,20 @@ A key may optionally be bound to an _action_ which accepts zero or more argument *overloadi(, overloadt2(, , ), )* *timeout(, , )* - If the key is held in isolation for more than _ ms_, activate the second - action, if the key is held for less than _ ms_ or another key is struck - before ms expires, execute the first action. + If no key events occur within _ ms_, execute , otherwise + execute . E.g. timeout(a, 500, layer(control)) - Will cause the assigned key to behave as _control_ if it is held for more than - 500 ms. + Will cause the assigned key to behave as control if it is held for more than + 500 ms without any keys being struck in the interval. + + *NOTE:* This is an older option with numerous subtle implications. It is + mainly intended to be used in combination with other actions to achieve + things which are not otherwise possible. Most users will want to use + one of overload functions for simple tap/hold behaviour. *macro2(, , )* Creates a macro with the given timeout and repeat timeout. If a timeout value of 0 is used, diff --git a/src/keyboard.c b/src/keyboard.c index 7e648a1..3e0705a 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -571,19 +571,16 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, uint8_t layer = d->args[0].idx; struct descriptor *action = &kbd->config.descriptors[d->args[1].idx]; - kbd->pending_key.code = code; - kbd->pending_key.behaviour = - d->op == OP_OVERLOAD_TIMEOUT_TAP ? - PK_UNINTERRUPTIBLE_TAP_ACTION2 : - PK_UNINTERRUPTIBLE; - - kbd->pending_key.dl = dl; - kbd->pending_key.action1 = *action; - kbd->pending_key.action2.op = OP_LAYER; - kbd->pending_key.action2.args[0].idx = layer; - kbd->pending_key.expire = time+d->args[2].timeout; - - schedule_timeout(kbd, kbd->pending_key.expire); + kbd->pending_overload.code = code; + kbd->pending_overload.resolve_on_interrupt = d->op == OP_OVERLOAD_TIMEOUT_TAP; + + kbd->pending_overload.dl = dl; + kbd->pending_overload.action1 = *action; + kbd->pending_overload.action2.op = OP_LAYER; + kbd->pending_overload.action2.args[0].idx = layer; + kbd->pending_overload.expiration = time + d->args[2].timeout; + + schedule_timeout(kbd, kbd->pending_overload.expiration); } break; @@ -720,18 +717,23 @@ static long process_descriptor(struct keyboard *kbd, uint8_t code, break; case OP_TIMEOUT: + struct pending_timeout *pt = &kbd->pending_timeout; + if (pressed) { - kbd->pending_key.action1 = kbd->config.descriptors[d->args[0].idx]; - kbd->pending_key.action2 = kbd->config.descriptors[d->args[2].idx]; + pt->code = code; + pt->dl = dl; - kbd->pending_key.code = code; - kbd->pending_key.dl = dl; - kbd->pending_key.expire = time + d->args[1].timeout; - kbd->pending_key.behaviour = PK_INTERRUPT_ACTION1; + pt->action1 = kbd->config.descriptors[d->args[0].idx]; + pt->expiration = time + d->args[1].timeout; + pt->action2 = kbd->config.descriptors[d->args[2].idx]; - schedule_timeout(kbd, kbd->pending_key.expire); - } + pt->activation_time = time; + pt->spontaneous = 0; + schedule_timeout(kbd, pt->expiration); + } else if (time == kbd->pending_timeout.activation_time) { + pt->spontaneous = 1; + } break; case OP_COMMAND: if (pressed) { @@ -1032,77 +1034,92 @@ static int handle_chord(struct keyboard *kbd, return 0; } -int handle_pending_key(struct keyboard *kbd, uint8_t code, int pressed, long time) +int handle_pending_timeout(struct keyboard *kbd, uint8_t event_code, int pressed, long time) { - if (!kbd->pending_key.code) + struct pending_timeout pt = kbd->pending_timeout; + + if (!pt.code || (!pressed && pt.code == event_code && time == pt.activation_time)) return 0; - struct descriptor action = {0}; + if (pt.spontaneous) { + if ((time >= pt.expiration) || event_code) { + struct descriptor action = time >= pt.expiration ? pt.action2 : pt.action1; + kbd->pending_timeout.code = 0; + + process_descriptor(kbd, pt.code, &action, pt.dl, 1, time); + process_descriptor(kbd, pt.code, &action, pt.dl, 0, time); + } + } else if (time >= pt.expiration || (event_code && (pressed || event_code == pt.code))) { + struct descriptor action = time >= pt.expiration ? pt.action2 : pt.action1; + kbd->pending_timeout.code = 0; + + cache_set(kbd, pt.code, &(struct cache_entry){ + .code = pt.code, + .dl = pt.dl, + .d = action, + }); + process_descriptor(kbd, pt.code, &action, pt.dl, 1, time); + } + + return 0; +} + +int handle_pending_overload(struct keyboard *kbd, uint8_t code, int pressed, long time) +{ + struct descriptor action; + + if (!kbd->pending_overload.code) + return 0; if (code) { struct key_event *ev; - assert(kbd->pending_key.queue_sz < ARRAY_SIZE(kbd->pending_key.queue)); + assert(kbd->pending_overload.queue_sz < ARRAY_SIZE(kbd->pending_overload.queue)); if (!pressed) { size_t i; int found = 0; - for (i = 0; i < kbd->pending_key.queue_sz; i++) - if (kbd->pending_key.queue[i].code == code) + for (i = 0; i < kbd->pending_overload.queue_sz; i++) + if (kbd->pending_overload.queue[i].code == code) found = 1; /* Propagate key up events for keys which were struck before the pending key. */ - if (!found && code != kbd->pending_key.code) + if (!found && code != kbd->pending_overload.code) return 0; } - ev = &kbd->pending_key.queue[kbd->pending_key.queue_sz]; + ev = &kbd->pending_overload.queue[kbd->pending_overload.queue_sz]; ev->code = code; ev->pressed = pressed; ev->timestamp = time; - kbd->pending_key.queue_sz++; + kbd->pending_overload.queue_sz++; } - if (time >= kbd->pending_key.expire) { - action = kbd->pending_key.action2; - } else if (code == kbd->pending_key.code) { - if (kbd->pending_key.tap_expiry && time >= kbd->pending_key.tap_expiry) { - action.op = OP_KEYSEQUENCE; - action.args[0].code = KEYD_NOOP; - } else { - action = kbd->pending_key.action1; - } - } else if (code && pressed && kbd->pending_key.behaviour == PK_INTERRUPT_ACTION1) { - action = kbd->pending_key.action1; - } else if (code && pressed && kbd->pending_key.behaviour == PK_INTERRUPT_ACTION2) { - action = kbd->pending_key.action2; - } else if (kbd->pending_key.behaviour == PK_UNINTERRUPTIBLE_TAP_ACTION2 && !pressed) { - size_t i; - - for (i = 0; i < kbd->pending_key.queue_sz; i++) - if (kbd->pending_key.queue[i].code == code) { - action = kbd->pending_key.action2; - break; - } - } + if (time >= kbd->pending_overload.expiration) + action = kbd->pending_overload.action2; + else if (code == kbd->pending_overload.code) + action = kbd->pending_overload.action1; + else if (kbd->pending_overload.resolve_on_interrupt && !pressed) + action = kbd->pending_overload.action2; + else + action.op = 0; if (action.op) { /* Create a copy of the queue on the stack to allow for recursive pending key processing. */ - struct key_event queue[ARRAY_SIZE(kbd->pending_key.queue)]; - size_t queue_sz = kbd->pending_key.queue_sz; + struct key_event queue[ARRAY_SIZE(kbd->pending_overload.queue)]; + size_t queue_sz = kbd->pending_overload.queue_sz; - uint8_t code = kbd->pending_key.code; - int dl = kbd->pending_key.dl; + uint8_t code = kbd->pending_overload.code; + int dl = kbd->pending_overload.dl; - memcpy(queue, kbd->pending_key.queue, sizeof kbd->pending_key.queue); + memcpy(queue, kbd->pending_overload.queue, sizeof kbd->pending_overload.queue); - kbd->pending_key.code = 0; - kbd->pending_key.queue_sz = 0; - kbd->pending_key.tap_expiry = 0; + kbd->pending_overload.code = 0; + kbd->pending_overload.queue_sz = 0; cache_set(kbd, code, &(struct cache_entry) { .d = action, @@ -1133,7 +1150,10 @@ static long process_event(struct keyboard *kbd, uint8_t code, int pressed, long if (handle_chord(kbd, code, pressed, time)) goto exit; - if (handle_pending_key(kbd, code, pressed, time)) + if (handle_pending_timeout(kbd, code, pressed, time)) + goto exit; + + if (handle_pending_overload(kbd, code, pressed, time)) goto exit; if (kbd->oneshot_timeout && time >= kbd->oneshot_timeout) { diff --git a/src/keyboard.h b/src/keyboard.h index dc94075..4b1c7a1 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -67,7 +67,7 @@ struct keyboard { long last_simple_key_time; - long timeouts[64]; + long timeouts[128]; size_t nr_timeouts; struct active_chord { @@ -94,25 +94,31 @@ struct keyboard { } state; } chord; - struct { + struct pending_timeout { uint8_t code; uint8_t dl; - long expire; - long tap_expiry; + uint8_t spontaneous; - enum { - PK_INTERRUPT_ACTION1, - PK_INTERRUPT_ACTION2, - PK_UNINTERRUPTIBLE, - PK_UNINTERRUPTIBLE_TAP_ACTION2, - } behaviour; + long expiration; + long activation_time; + + struct descriptor action1; + struct descriptor action2; + } pending_timeout; + + struct pending_overload { + uint8_t code; + uint8_t dl; + long expiration; + + int resolve_on_interrupt; struct key_event queue[32]; size_t queue_sz; struct descriptor action1; struct descriptor action2; - } pending_key; + } pending_overload; struct { long activation_time; diff --git a/t/double-tap.t b/t/double-tap.t new file mode 100644 index 0000000..c904619 --- /dev/null +++ b/t/double-tap.t @@ -0,0 +1,24 @@ +up down +1ms +up up +up down +1ms +up up +up down +200ms +up up +up down +1ms +up up +100ms +x down +x up + +a down +a up +c down +c up +b down +b up +x down +x up diff --git a/t/test.conf b/t/test.conf index 03bd453..d6cec40 100644 --- a/t/test.conf +++ b/t/test.conf @@ -6,7 +6,6 @@ [global] -chord_interkey_timeout = 100 chord_hold_timeout = 200 overload_tap_timeout = 5 @@ -29,7 +28,7 @@ p = layerm(shift, macro(on)) 5 = layer(symbols) 6 = overload(6l, esc) 7 = overload(meta, oneshot(control)) -8 = timeout(overload(control, a), 1, b) +8 = timeout(overload(control, a), 2, b) 9 = M-C-S-x 1+2 = oneshot(test) l = layer(test) @@ -45,6 +44,14 @@ o = overloadt(control, a, 10) [ = togglem(control, macro(one)) z = overload(control, enter) / = z +left = timeout(a, 100, timeout(b, 200, c)) +right = timeout(timeout(a, 100, b), 200, c) +up = timeout(timeout(oneshot(double), 100, b), 200, c) +delete = overloadt(control, timeout(a, 100, b), 100) + +[double] + +up = a [altgr] diff --git a/t/timeout-nested-1.t b/t/timeout-nested-1.t new file mode 100644 index 0000000..41b8ef9 --- /dev/null +++ b/t/timeout-nested-1.t @@ -0,0 +1,20 @@ +left down +99ms +left up +left down +299ms +left up +left down +300ms +left up +x down +x up + +a down +a up +b down +b up +c down +c up +x down +x up diff --git a/t/timeout-nested-2.t b/t/timeout-nested-2.t new file mode 100644 index 0000000..f0bd1ff --- /dev/null +++ b/t/timeout-nested-2.t @@ -0,0 +1,31 @@ +right down +200ms +right up +right down +199ms +right up +99ms +x down +x up +right down +200ms +right up +right down +199ms +right up +100ms +x down +x up + +c down +c up +a down +a up +x down +x up +c down +c up +b down +b up +x down +x up diff --git a/t/timeout-overloadt.t b/t/timeout-overloadt.t new file mode 100644 index 0000000..b661927 --- /dev/null +++ b/t/timeout-overloadt.t @@ -0,0 +1,21 @@ +delete down +100ms +delete up +delete down +99ms +delete up +delete down +99ms +delete up +100ms +x down +x up + +leftcontrol down +leftcontrol up +a down +a up +b down +b up +x down +x up diff --git a/t/timeout1.t b/t/timeout1.t index c0b63d0..77107fe 100644 --- a/t/timeout1.t +++ b/t/timeout1.t @@ -10,6 +10,11 @@ x down 200ms = up x up += down +300ms +x down += up +x up a down a up @@ -19,3 +24,7 @@ a down x down a up x up +b down +x down +b up +x up diff --git a/t/timeout2.t b/t/timeout2.t index cb41267..fad15d3 100644 --- a/t/timeout2.t +++ b/t/timeout2.t @@ -3,6 +3,7 @@ x down x up 8 up 8 down +1ms 8 up 8 down 2ms diff --git a/t/timeout3.t b/t/timeout3.t new file mode 100644 index 0000000..3eef4f6 --- /dev/null +++ b/t/timeout3.t @@ -0,0 +1,19 @@ += down += up +299ms +x down +x up += down += up +300ms +x down +x up + +a down +a up +x down +x up +b down +b up +x down +x up