refactor gesture handling to use timers and callbacks instead of loops
This commit is contained in:
+61
-78
@@ -12,50 +12,20 @@ class WhisplayInterface:
|
||||
def __init__(self):
|
||||
self.board = None
|
||||
self._lock = threading.Lock()
|
||||
self._is_pressing = False
|
||||
self._is_double_pressing = False
|
||||
self._is_long_pressing = False
|
||||
self._long_press_timer_thread = None
|
||||
self._button_press_start = None
|
||||
self._button_press_end = None
|
||||
self._button_is_pressed = False
|
||||
self._button_was_pressed = False
|
||||
self._button_is_long_pressed = False
|
||||
self._button_was_long_pressed = False
|
||||
self._button_was_double_pressed = False
|
||||
self._on_press_callback = None
|
||||
self._on_release_callback = None
|
||||
self._on_long_press_callback = None
|
||||
self._on_long_release_callback = None
|
||||
self._on_double_press_callback = None
|
||||
self._on_double_release_callback = None
|
||||
|
||||
# Edge-detection state
|
||||
self._prev_is_pressed = False
|
||||
self._prev_press_end = None
|
||||
|
||||
self.set_wm8960_volume_stable(speaker=250, mic=30)
|
||||
|
||||
# -- Properties --
|
||||
|
||||
@property
|
||||
def is_pressed(self):
|
||||
return self._button_is_pressed
|
||||
|
||||
@property
|
||||
def was_pressed(self):
|
||||
return self._button_was_pressed
|
||||
|
||||
@property
|
||||
def is_long_pressed(self):
|
||||
return self._button_is_long_pressed
|
||||
|
||||
@property
|
||||
def was_long_pressed(self):
|
||||
return self._button_was_long_pressed
|
||||
|
||||
@property
|
||||
def was_double_pressed(self):
|
||||
return self._button_was_double_pressed
|
||||
|
||||
@property
|
||||
def press_duration(self):
|
||||
if self._button_press_start is None:
|
||||
return 0.0
|
||||
end = self._button_press_end if self._button_press_end is not None else time.time()
|
||||
return end - self._button_press_start
|
||||
|
||||
# -- Attachment --
|
||||
self.set_wm8960_volume_stable(speaker=200, mic=80)
|
||||
|
||||
def create_board(self):
|
||||
self.board = runtime.whisplay_client.create_whisplay_hardware(
|
||||
@@ -70,53 +40,66 @@ class WhisplayInterface:
|
||||
|
||||
# -- Internal press/release (called from GPIO thread) --
|
||||
|
||||
def on_press(self, callback):
|
||||
self._on_press_callback = callback
|
||||
|
||||
def on_release(self, callback):
|
||||
self._on_release_callback = callback
|
||||
|
||||
def on_long_press(self, callback):
|
||||
self._on_long_press_callback = callback
|
||||
|
||||
def on_long_release(self, callback):
|
||||
self._on_long_release_callback = callback
|
||||
|
||||
def on_double_press(self, callback):
|
||||
self._on_double_press_callback = callback
|
||||
|
||||
def on_double_release(self, callback):
|
||||
self._on_double_release_callback = callback
|
||||
|
||||
|
||||
def _on_button_press(self):
|
||||
with self._lock:
|
||||
self._button_is_pressed = True
|
||||
self._is_pressing = True
|
||||
self._button_press_start = time.time()
|
||||
if self._button_press_end is not None:
|
||||
time_since_last_press = self._button_press_start- self._button_press_end
|
||||
if time_since_last_press <= self.DOUBLE_PRESS_THRESHOLD:
|
||||
self._is_double_pressing = True
|
||||
if self._on_double_press_callback:
|
||||
self._on_double_press_callback()
|
||||
if not self._is_double_pressing:
|
||||
if self._on_press_callback:
|
||||
self._on_press_callback()
|
||||
self._long_press_timer_thread = threading.Timer(self.LONG_PRESS_THRESHOLD, self._long_press_timer)
|
||||
self._long_press_timer_thread.start()
|
||||
|
||||
|
||||
def _on_button_release(self):
|
||||
with self._lock:
|
||||
self._button_is_pressed = False
|
||||
self._long_press_timer_thread.cancel()
|
||||
self._is_pressing = False
|
||||
self._button_press_end = time.time()
|
||||
if self._is_double_pressing:
|
||||
if self._on_double_release_callback:
|
||||
self._on_double_release_callback()
|
||||
self._is_double_pressing = False
|
||||
elif self._is_long_pressing:
|
||||
if self._on_long_release_callback:
|
||||
self._on_long_release_callback()
|
||||
self._is_long_pressing = False
|
||||
else:
|
||||
if self._on_release_callback:
|
||||
self._on_release_callback()
|
||||
|
||||
# -- Polled update (called from gesture thread) --
|
||||
|
||||
def update_gestures(self):
|
||||
self._button_was_pressed = False
|
||||
self._button_was_long_pressed = False
|
||||
self._button_was_double_pressed = False
|
||||
|
||||
def _long_press_timer(self):
|
||||
with self._lock:
|
||||
is_pressed = self._button_is_pressed
|
||||
press_start = self._button_press_start
|
||||
press_end = self._button_press_end
|
||||
if self._is_pressing:
|
||||
self._is_long_pressing = True
|
||||
if self._on_long_press_callback:
|
||||
self._on_long_press_callback()
|
||||
|
||||
now = time.time()
|
||||
|
||||
# Press edge: transitioned from not-pressed to pressed
|
||||
if not self._prev_is_pressed and is_pressed:
|
||||
if self._prev_press_end is not None and press_start is not None:
|
||||
gap = press_start - self._prev_press_end
|
||||
if gap < WhisplayInterface.DOUBLE_PRESS_THRESHOLD:
|
||||
self._button_was_double_pressed = True
|
||||
|
||||
# Release edge: transitioned from pressed to not-pressed
|
||||
if self._prev_is_pressed and not is_pressed:
|
||||
self._button_was_pressed = True
|
||||
if press_start is not None and press_end is not None:
|
||||
duration = press_end - press_start
|
||||
if duration >= WhisplayInterface.LONG_PRESS_THRESHOLD:
|
||||
self._button_was_long_pressed = True
|
||||
self._prev_press_end = press_end
|
||||
|
||||
# Level flag: currently held past threshold
|
||||
if is_pressed and press_start is not None:
|
||||
self._button_is_long_pressed = (now - press_start) >= WhisplayInterface.LONG_PRESS_THRESHOLD
|
||||
else:
|
||||
self._button_is_long_pressed = False
|
||||
|
||||
self._prev_is_pressed = is_pressed
|
||||
|
||||
def set_wm8960_volume_stable(self, speaker: int = 0, mic: int = 0):
|
||||
CARD_NAME = 'wm8960soundcard'
|
||||
|
||||
Reference in New Issue
Block a user