Skip to content

Commit 93451ee

Browse files
Mearmanclaude
andcommitted
fw/services/touch: release an active touch when the last subscriber leaves
The last subscriber going away powers the touch sensor down mid-gesture, so a finger that was down at teardown never produces a Liftoff. The Touchdown's backlight hold (light_touch_down -> light_button_pressed) then never unwinds: the button refcount stays pinned, the light never enters its timed state, and with the sensor off no later touch can clear it. Synthesise the Liftoff with the last coordinates when the subscriber count reaches zero, mirroring the teardown the global disable path already performs for the same reason. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Joseph Mearman <joseph@mearman.co.uk>
1 parent e7fe4f6 commit 93451ee

2 files changed

Lines changed: 28 additions & 5 deletions

File tree

src/fw/services/touch/touch.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,22 @@ static void prv_add_subscriber_cb(PebbleTask task) {
6363
static void prv_remove_subscriber_cb(PebbleTask task) {
6464
mutex_lock(s_touch_mutex);
6565
PBL_ASSERTN(s_subscriber_count > 0);
66-
if (--s_subscriber_count == 0 && s_globally_enabled) {
66+
const bool was_last_subscriber = (--s_subscriber_count == 0);
67+
if (was_last_subscriber && s_globally_enabled) {
6768
touch_sensor_set_enabled(false);
6869
}
69-
// An app whose shared touch subscription disappears (task exit, or both
70-
// slots emptied) cannot have a live raw handler or nav dispatcher anymore;
71-
// drop its raw-slot mark and the app-nav-active flag so a crashed app cannot
72-
// leak backlight-follow behavior.
70+
// An app whose shared touch subscription disappears (task exit, or both slots emptied) cannot have a live raw handler or nav dispatcher anymore; drop its raw-slot mark and the app-nav-active flag so a crashed app cannot leak backlight-follow behavior.
7371
if (task == PebbleTask_App) {
7472
s_raw_subscriber_tasks &= (uint8_t)~(1u << task);
7573
s_app_nav_active = false;
7674
}
7775
PBL_LOG_DBG("Touch: subscriber removed, count=%" PRIu8, s_subscriber_count);
7876
mutex_unlock(s_touch_mutex);
77+
78+
if (was_last_subscriber) {
79+
// A finger down when the last subscriber goes away never gets a Liftoff otherwise (the sensor stops reporting), so the backlight hold counter stays pinned. Same teardown the global toggle performs.
80+
touch_release_active();
81+
}
7982
}
8083

8184
void touch_init(void) {

tests/fw/services/test_touch.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,26 @@ void test_touch__reset_mid_gesture_refuses_continuation(void) {
591591
touch_handle_injected_update(TouchInjectPhase_End, 50, 60);
592592
}
593593

594+
void test_touch__last_unsubscribe_with_finger_down_emits_liftoff(void) {
595+
// Finger down, then the last subscriber goes away: the sensor powers down mid-gesture, so a Liftoff must be synthesized with the last coordinates (not zeros) or the backlight hold taken by the Touchdown never unwinds — the same teardown the global toggle performs.
596+
s_add_subscriber_cb(PebbleTask_App);
597+
touch_handle_update(TouchState_FingerDown, 30, 40);
598+
fake_event_reset_count();
599+
600+
s_remove_subscriber_cb(PebbleTask_App);
601+
cl_assert_equal_i(fake_event_get_count(), 1);
602+
prv_assert_touch_event(TouchEvent_Liftoff, 30, 40);
603+
}
604+
605+
void test_touch__last_unsubscribe_without_finger_no_liftoff(void) {
606+
// No finger down: the last subscriber leaving must not fabricate a Liftoff.
607+
s_add_subscriber_cb(PebbleTask_App);
608+
fake_event_reset_count();
609+
610+
s_remove_subscriber_cb(PebbleTask_App);
611+
cl_assert_equal_i(fake_event_get_count(), 0);
612+
}
613+
594614
void test_touch__event_abi_unchanged(void) {
595615
// non_navigational rides in the padding after type:8; x/y offsets and the
596616
// overall size must not move, keeping the SDK struct app-compatible.

0 commit comments

Comments
 (0)