From 173c0a6a4f4fd269b95af7f91993645fbe02c354 Mon Sep 17 00:00:00 2001 From: Josiah Date: Mon, 31 Aug 2026 16:49:31 -0400 Subject: [PATCH 1/2] drivers/touch/cst816: report touchscreen "palm" gesture based on my testing, the CST816 appears to report a palm covering the screen as gesture 0xAA. it is not in the documented gesture list; it was found by dumping the chip's register block on device while I tried a number of different touch gestures. this is now reported by the driver Co-Authored-By: Claude Opus 5 Signed-off-by: Josiah --- include/pbl/services/touch/gesture_event.h | 1 + include/pbl/services/touch/touch.h | 1 + src/fw/drivers/touch/cst816/cst816.c | 16 +++++++++++++++- src/fw/services/touch/touch.c | 3 +++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/pbl/services/touch/gesture_event.h b/include/pbl/services/touch/gesture_event.h index 069f326335..4bdf8d7986 100644 --- a/include/pbl/services/touch/gesture_event.h +++ b/include/pbl/services/touch/gesture_event.h @@ -9,6 +9,7 @@ typedef enum GestureEventType { GestureEvent_Tap, GestureEvent_DoubleTap, + GestureEvent_Palm, } GestureEventType; //! Gesture event data, carried directly in PebbleGestureEvent diff --git a/include/pbl/services/touch/touch.h b/include/pbl/services/touch/touch.h index 7446615def..37c02e0a1e 100644 --- a/include/pbl/services/touch/touch.h +++ b/include/pbl/services/touch/touch.h @@ -16,6 +16,7 @@ typedef enum TouchState { typedef enum TouchGesture { TouchGesture_Tap, TouchGesture_DoubleTap, + TouchGesture_Palm, } TouchGesture; void touch_init(void); diff --git a/src/fw/drivers/touch/cst816/cst816.c b/src/fw/drivers/touch/cst816/cst816.c index dda48f8c63..62f9f87151 100644 --- a/src/fw/drivers/touch/cst816/cst816.c +++ b/src/fw/drivers/touch/cst816/cst816.c @@ -42,6 +42,7 @@ PBL_LOG_MODULE_DEFINE(driver_touch_cst816, CONFIG_DRIVER_TOUCH_LOG_LEVEL); #define CST816_GESTURE_CLICK 0x05 #define CST816_GESTURE_DOUBLE_CLICK 0x0B #define CST816_GESTURE_LONG_PRESS 0x0C +#define CST816_GESTURE_PALM 0xAA #define CST816_BOOT_MODE_REG 0xA001 #define CST816_BOOT_MODE_CMD 0xAB @@ -68,6 +69,7 @@ PBL_LOG_MODULE_DEFINE(driver_touch_cst816, CONFIG_DRIVER_TOUCH_LOG_LEVEL); #define CST816_WAKE_SPACING_MS 2000 static bool s_callback_scheduled = false; +static bool s_palm_down = false; static bool s_enabled = false; static bool s_reset_scheduled = false; static bool s_activity_since_check = false; @@ -342,7 +344,18 @@ static void prv_process_pending_messages(void* context) { break; } - if (press == 0x01) { + // The gesture register holds its last value, so a palm reads back as one on + // every sample it produces, its liftoff included; latch it instead. + if (press != 0x01) { + s_palm_down = false; + } else if (id == CST816_GESTURE_PALM && !s_palm_down) { + s_palm_down = true; + touch_handle_gesture(TouchGesture_Palm, point.x, point.y); + } + + // A palm also reports a contact at its centroid, which would take the + // backlight the gesture just dropped and land as a tap underneath it. + if (press == 0x01 && !s_palm_down) { touch_handle_update(TouchState_FingerDown, point.x, point.y); } else { touch_handle_update(TouchState_FingerUp, point.x, point.y); @@ -390,6 +403,7 @@ static void prv_watchdog_cb(void *data) { void touch_sensor_set_enabled(bool enabled) { cst816_hw_reset(); + s_palm_down = false; if (enabled) { exti_enable(CST816->int_exti); diff --git a/src/fw/services/touch/touch.c b/src/fw/services/touch/touch.c index 637626deb7..939f1771b6 100644 --- a/src/fw/services/touch/touch.c +++ b/src/fw/services/touch/touch.c @@ -361,6 +361,9 @@ void touch_handle_gesture(TouchGesture gesture, int16_t x, int16_t y) { PBL_ANALYTICS_ADD(gesture_double_tap_count, 1); prv_put_gesture_event(GestureEvent_DoubleTap, x, y); break; + case TouchGesture_Palm: + prv_put_gesture_event(GestureEvent_Palm, x, y); + break; default: break; } From fe80e7f3625d8ac01dafd240c6bc888039e3a33c Mon Sep 17 00:00:00 2001 From: Josiah Date: Mon, 31 Aug 2026 16:51:41 -0400 Subject: [PATCH 2/2] shell/services: palm turns backlight off adds a new global touch gesture and corresponding settings toggle - tapping the screen with your palm now immediately turns off the backlight, as if the timer expired. this mirrors the gesture functionality of many mainstream smartwatches. Co-Authored-By: Claude Opus 5 Signed-off-by: Josiah --- include/pbl/services/light.h | 5 +++++ src/fw/apps/system/settings/display.c | 15 ++++++++++++--- src/fw/kernel/event_loop.c | 12 ++++++++++++ src/fw/services/light/service.c | 11 +++++++++++ src/fw/shell/normal/prefs.c | 16 ++++++++++++++++ src/fw/shell/normal/prefs_values.h.inc | 1 + src/fw/shell/prefs.h | 4 ++++ src/fw/shell/prf/stubs.c | 7 +++++++ src/fw/shell/sdk/stubs.c | 7 +++++++ 9 files changed, 75 insertions(+), 3 deletions(-) diff --git a/include/pbl/services/light.h b/include/pbl/services/light.h index 857bce5af1..aa286c6aa8 100644 --- a/include/pbl/services/light.h +++ b/include/pbl/services/light.h @@ -43,6 +43,11 @@ void light_touch_up(void); //! @copydoc app_light_enable void light_enable(bool enable); +//! @internal +//! Drop the backlight now, as if its timeout had expired. Unlike +//! light_enable(false), does not claim app control of the light. +void light_off_now(void); + //! @internal //! light_enable that adheres to user's backlight setting. void light_enable_respect_settings(bool enable); diff --git a/src/fw/apps/system/settings/display.c b/src/fw/apps/system/settings/display.c index 6633838691..2571c8e1cd 100644 --- a/src/fw/apps/system/settings/display.c +++ b/src/fw/apps/system/settings/display.c @@ -350,6 +350,7 @@ enum SettingsBacklightItem { SettingsBacklightMotionWake, #ifdef CONFIG_TOUCH SettingsBacklightTouchWake, + SettingsBacklightPalmSleep, #endif SettingsBacklightTimeout, NumSettingsBacklightItems @@ -362,10 +363,11 @@ static bool prv_backlight_item_is_visible(uint16_t item) { // Always shown, even when the backlight is off. return true; #ifdef CONFIG_TOUCH - // The wake-on-touch row is only relevant when global touch is enabled. - // It gets hidden dynamically (not just gated at compile time) so users - // don't see a dangling backlight option that can't do anything. + // The touch rows are only relevant when global touch is enabled. They get + // hidden dynamically (not just gated at compile time) so users don't see a + // dangling backlight option that can't do anything. case SettingsBacklightTouchWake: + case SettingsBacklightPalmSleep: return backlight_is_enabled() && touch_is_globally_enabled(); #endif default: @@ -400,6 +402,9 @@ static void prv_backlight_select_click_cb(SettingsCallbacks *context, uint16_t r case SettingsBacklightTouchWake: prv_touch_wake_menu_push(data); break; + case SettingsBacklightPalmSleep: + backlight_set_palm_sleep_enabled(!backlight_is_palm_sleep_enabled()); + break; #endif case SettingsBacklightAmbientSensor: light_toggle_ambient_sensor_enabled(); @@ -456,6 +461,10 @@ static void prv_backlight_draw_row_cb(SettingsCallbacks *context, GContext *ctx, title = i18n_noop("Wake on touch"); subtitle = s_touch_wake_labels[backlight_get_touch_wake()]; break; + case SettingsBacklightPalmSleep: + title = i18n_noop("Palm to sleep"); + subtitle = backlight_is_palm_sleep_enabled() ? i18n_noop("On") : i18n_noop("Off"); + break; #endif case SettingsBacklightAmbientSensor: title = i18n_noop("Ambient Sensor"); diff --git a/src/fw/kernel/event_loop.c b/src/fw/kernel/event_loop.c index 8367a8fd60..f1796e46a7 100644 --- a/src/fw/kernel/event_loop.c +++ b/src/fw/kernel/event_loop.c @@ -358,6 +358,18 @@ static NOINLINE void prv_minimal_event_handler(PebbleEvent* e) { #endif case PEBBLE_GESTURE_EVENT: { + if (e->gesture.event.type == GestureEvent_Palm) { + if (backlight_is_palm_sleep_enabled()) { + light_off_now(); +#ifdef CONFIG_TOUCH + // The screen was deliberately put to sleep, so close the interaction + // session too: a stray touch afterwards must not navigate. + touch_session_reset(); +#endif + } + return; + } + bool wake_on_gesture = false; switch (backlight_get_touch_wake()) { case BacklightTouchWake_Tap: diff --git a/src/fw/services/light/service.c b/src/fw/services/light/service.c index 24b23496e7..87cf420471 100644 --- a/src/fw/services/light/service.c +++ b/src/fw/services/light/service.c @@ -592,6 +592,17 @@ void light_enable(bool enable) { mutex_unlock(s_mutex); } +void light_off_now(void) { + mutex_lock(s_mutex); + + if (s_num_buttons_down == 0) { + s_user_controlled_state = false; + prv_change_state(LIGHT_STATE_OFF); + } + + mutex_unlock(s_mutex); +} + void light_enable_respect_settings(bool enable) { mutex_lock(s_mutex); diff --git a/src/fw/shell/normal/prefs.c b/src/fw/shell/normal/prefs.c index 294cd4f54b..8aa3522d25 100644 --- a/src/fw/shell/normal/prefs.c +++ b/src/fw/shell/normal/prefs.c @@ -91,6 +91,9 @@ static bool s_backlight_motion_enabled = true; #define PREF_KEY_BACKLIGHT_TOUCH "lightTouch" static uint8_t s_backlight_touch_wake = BacklightTouchWake_DoubleTap; +#define PREF_KEY_BACKLIGHT_PALM_SLEEP "lightPalmSleep" +static bool s_backlight_palm_sleep = true; + #define PREF_KEY_TOUCH_ENABLED "touchEnabled" static bool s_touch_enabled = true; @@ -440,6 +443,11 @@ static bool prv_set_s_backlight_touch_wake(uint8_t *wake) { return true; } +static bool prv_set_s_backlight_palm_sleep(bool *enable) { + s_backlight_palm_sleep = *enable; + return true; +} + #ifdef CONFIG_TOUCH // System touch navigation is active only while BOTH prefs are on: the master // "Touch" switch (the global touch kill, PREF_KEY_TOUCH_ENABLED) and the @@ -1378,6 +1386,14 @@ void backlight_set_touch_wake(BacklightTouchWake wake) { prv_pref_set(PREF_KEY_BACKLIGHT_TOUCH, &value, sizeof(value)); } +bool backlight_is_palm_sleep_enabled(void) { + return s_backlight_palm_sleep; +} + +void backlight_set_palm_sleep_enabled(bool enable) { + prv_pref_set(PREF_KEY_BACKLIGHT_PALM_SLEEP, &enable, sizeof(enable)); +} + bool touch_is_globally_enabled(void) { return s_touch_enabled; } diff --git a/src/fw/shell/normal/prefs_values.h.inc b/src/fw/shell/normal/prefs_values.h.inc index 05f4a20501..0449bfe84a 100644 --- a/src/fw/shell/normal/prefs_values.h.inc +++ b/src/fw/shell/normal/prefs_values.h.inc @@ -13,6 +13,7 @@ #endif PREFS_MACRO(PREF_KEY_BACKLIGHT_MOTION, s_backlight_motion_enabled) PREFS_MACRO(PREF_KEY_BACKLIGHT_TOUCH, s_backlight_touch_wake) + PREFS_MACRO(PREF_KEY_BACKLIGHT_PALM_SLEEP, s_backlight_palm_sleep) PREFS_MACRO(PREF_KEY_TOUCH_ENABLED, s_touch_enabled) PREFS_MACRO(PREF_KEY_TOUCH_NAVIGATION_MENU, s_touch_navigation_menu_enabled) PREFS_MACRO(PREF_KEY_MOTION_SENSITIVITY, s_motion_sensitivity) diff --git a/src/fw/shell/prefs.h b/src/fw/shell/prefs.h index f4475bb478..bc9429ce39 100644 --- a/src/fw/shell/prefs.h +++ b/src/fw/shell/prefs.h @@ -105,6 +105,10 @@ typedef enum BacklightTouchWake { BacklightTouchWake backlight_get_touch_wake(void); void backlight_set_touch_wake(BacklightTouchWake wake); +// Whether covering the screen with a palm turns the backlight off. +bool backlight_is_palm_sleep_enabled(void); +void backlight_set_palm_sleep_enabled(bool enable); + // Global touch input kill-switch. When false, the kernel touch service // drops events at the source, powers the sensor down, and the applib // touch_service_is_enabled() query returns false to apps. diff --git a/src/fw/shell/prf/stubs.c b/src/fw/shell/prf/stubs.c index f8a2d9a258..db55dc0e1a 100644 --- a/src/fw/shell/prf/stubs.c +++ b/src/fw/shell/prf/stubs.c @@ -229,6 +229,13 @@ BacklightTouchWake backlight_get_touch_wake(void) { void backlight_set_touch_wake(BacklightTouchWake wake) { } +bool backlight_is_palm_sleep_enabled(void) { + return false; +} + +void backlight_set_palm_sleep_enabled(bool enable) { +} + bool touch_is_globally_enabled(void) { return true; } diff --git a/src/fw/shell/sdk/stubs.c b/src/fw/shell/sdk/stubs.c index 0ceb6a4077..e826e8a2d4 100644 --- a/src/fw/shell/sdk/stubs.c +++ b/src/fw/shell/sdk/stubs.c @@ -73,6 +73,13 @@ BacklightTouchWake backlight_get_touch_wake(void) { void backlight_set_touch_wake(BacklightTouchWake wake) { } +bool backlight_is_palm_sleep_enabled(void) { + return false; +} + +void backlight_set_palm_sleep_enabled(bool enable) { +} + bool touch_is_globally_enabled(void) { return true; }