From 07a485b04d533df11aa3993ff158dd62bc765c2f Mon Sep 17 00:00:00 2001 From: xs5871 Date: Wed, 9 Mar 2022 01:22:09 +0000 Subject: [PATCH] implement stricter and more consistent timeouts. - stricter timeouts: Instead of adding an entire millisecond, use a list of timeouts that are supposed to be handled on the same tick. This reduces the delay between target tick and actual tick, especially for many timeouts issued to the same tick, i.e. combos. - consistent timeouts: Timeouts are now guaranteed to be handled in the order they were issued. Timer rollover is handled properly. caveat / change to the interface: the returned `timeout_key` is a tuple of the target timeout tick and the index of the timeout at that tick. --- kmk/kmk_keyboard.py | 40 ++++++++++++++++++++++++++-------------- kmk/kmktime.py | 4 ++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/kmk/kmk_keyboard.py b/kmk/kmk_keyboard.py index a3e34a8..9c05d96 100644 --- a/kmk/kmk_keyboard.py +++ b/kmk/kmk_keyboard.py @@ -3,6 +3,7 @@ from supervisor import ticks_ms from kmk.consts import KMK_RELEASE, UnicodeMode from kmk.hid import BLEHID, USBHID, AbstractHID, HIDModes from kmk.keys import KC +from kmk.kmktime import ticks_add, ticks_diff from kmk.matrix import MatrixScanner, intify_coordinate @@ -210,17 +211,22 @@ class KMKKeyboard: # We allow passing False as an implicit "run this on the next process timeouts cycle" timeout_key = ticks_ms() else: - timeout_key = ticks_ms() + after_ticks + timeout_key = ticks_add(ticks_ms(), after_ticks) - while timeout_key in self._timeouts: - timeout_key += 1 + if timeout_key not in self._timeouts: + self._timeouts[timeout_key] = [] - self._timeouts[timeout_key] = callback - return timeout_key + idx = len(self._timeouts[timeout_key]) + self._timeouts[timeout_key].append(callback) + + return (timeout_key, idx) def cancel_timeout(self, timeout_key): - if timeout_key in self._timeouts: - del self._timeouts[timeout_key] + try: + self._timeouts[timeout_key[0]][timeout_key[1]] = None + except (KeyError, IndexError): + if self.debug_enabled: + print(f'no such timeout: {timeout_key}') def _process_timeouts(self): if not self._timeouts: @@ -228,14 +234,20 @@ class KMKKeyboard: current_time = ticks_ms() - # cast this to a tuple to ensure that if a callback itself sets - # timeouts, we do not handle them on the current cycle - timeouts = tuple(self._timeouts.items()) + # Copy to a temporary list to allow sorting and ensure that if a + # callback itself sets timeouts, we do not handle them on the current + # cycle. + timeouts = [] + for k, v in self._timeouts.items(): + if ticks_diff(k, current_time) <= 0: + timeouts.append((k, v)) - for k, v in timeouts: - if k <= current_time: - v() - self.cancel_timeout(k) + timeouts.sort(key=lambda k: k[0]) + for k, callbacks in timeouts: + del self._timeouts[k] + for callback in callbacks: + if callback: + callback() return self diff --git a/kmk/kmktime.py b/kmk/kmktime.py index c4ef99b..965de65 100644 --- a/kmk/kmktime.py +++ b/kmk/kmktime.py @@ -12,6 +12,10 @@ def ticks_diff(new, start): return diff +def ticks_add(ticks, delta): + return (ticks + delta) % _TICKS_PERIOD + + def check_deadline(new, start, ms): return ticks_diff(new, start) < ms