Skip to content

Watchface selector on touchscreen long press - #1813

Draft
ericmigi wants to merge 4 commits into
coredevices:mainfrom
ericmigi:watchface-longpress-selector
Draft

Watchface selector on touchscreen long press#1813
ericmigi wants to merge 4 commits into
coredevices:mainfrom
ericmigi:watchface-longpress-selector

Conversation

@ericmigi

@ericmigi ericmigi commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

Hold a finger on the watchface for 2 s (touchscreen platforms only, e.g. obelix/getafix) to open a new carousel selector UI in the Watchfaces app:

  • Full-screen card per installed watchface: icon, name, Active badge, position counter, up/down arrows (content_indicator_draw_arrow).
  • Swipe up/down slides between watchfaces (with wrap-around); the face landed on is immediately persisted as the new default, so backing out returns to it.
  • Tap or SELECT launches the face (APP_LAUNCH_USER, so app_manager persists it as default). BACK exits. UP/DOWN buttons also navigate.
  • Entered only via WatchfacesLaunchArgs.selector_mode from the shell long-press hook; the regular Watchfaces menu list is unchanged.

Implementation:

  • src/fw/shell/normal/watchface.c: kernel-side touch subscription + 2 s hold timer (20 px slop cancel; suppressed while a modal is focused, in low power, or when a non-watchface is foreground).
  • src/fw/apps/system/watchfaces.c: the selector UI. Touch contacts starting within 500 ms of window load are ignored so the entry long-press finger is not read as a tap on real hardware.
  • All touch code is gated on CONFIG_TOUCH; non-touch platforms compile it out (verified with a qemu_flint build).

Also fixes a regression found while testing: touch_has_app_subscribers() counted any kernel-side subscriber as an app, so the new permanent subscription made the event loop light the backlight on every touchdown and bypass the touch-wake preference. Fixed with a new touch_set_system_subscribed() opt-out that only the watchface long-press hook uses -- the touchdown-driven backlight for third-party touch apps is unchanged (unit test added).

Power

  • Master Touch setting off: chip fully sleeps, feature disabled — unchanged.
  • Touch on + touch-wake enabled (default): chip was already powered 24/7 for the backlight subscription — zero delta.
  • Touch on + touch-wake off: chip now idles in its auto-sleep mode (µA) instead of deep sleep so the long press works; known tradeoff, fallback is a foreground-only subscription if telemetry flags it.

Testing

  • Verified end-to-end in the getafix QEMU emulator (qemu_gabbro) with injected touch: long-press open, swipe navigation + wrap, swipe-persists-default, tap-launch, back-exit, drag-slop cancel, button navigation.
  • ./pbl test suite green; new test_touch__has_app_subscribers_system fails before the backlight fix and passes after.
  • Dual-slot getafix dvt2 PBZ built and sideloaded for on-device testing.

🤖 Generated with Claude Code

ericmigi and others added 3 commits July 27, 2026 22:28
Add a second UI to the Watchfaces app: a full-screen carousel entered
only via WatchfacesLaunchArgs.selector_mode (APP_LAUNCH_SYSTEM). Each
card shows the watchface icon, name, Active badge and position counter.

Swiping left/right slides between installed watchfaces and immediately
persists the landed face as the default, so backing out returns to it.
Tapping the card or pressing SELECT launches the face with
APP_LAUNCH_USER. UP/DOWN also navigate, with wrap-around.

Touch contacts starting within 500 ms of window load are ignored so the
long-press finger that opened the selector is not treated as a tap when
it lifts. The args pointer is only interpreted when the launch reason is
APP_LAUNCH_SYSTEM, since quick launch passes integer action codes
through the same args field. Only compiled when CONFIG_TOUCH is set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Eric Migicovsky <ericmigi@gmail.com>
Subscribe to the touch service from the kernel watchface module and
launch the Watchfaces app in selector mode when a finger is held on the
watchface for two seconds. Movement beyond a small slop radius or
liftoff cancels the hold, and the launch is suppressed while a modal is
focused, in low power mode, or when the foreground app is not a
watchface.

The kernel-side subscription keeps the touch sensor powered even when
touch-to-backlight is off; the touch settings kill switch still disables
everything. Only compiled when CONFIG_TOUCH is set, so platforms without
a touchscreen are unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Eric Migicovsky <ericmigi@gmail.com>
Move the navigation arrows to the top and bottom (drawn with
content_indicator_draw_arrow), slide cards vertically, and navigate with
swipe up/down only; horizontal swipes are ignored. The position counter
moves under the Active badge to stay clear of the bottom arrow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Eric Migicovsky <ericmigi@gmail.com>
@ericmigi

ericmigi commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

@jplexer not urgent, but the next time you're doing some power testing on a ppk2, could you try running this for 1 min and benchmark power consumption compared to regular fw/same watchface?

@ericmigi
ericmigi requested a review from jplexer July 28, 2026 23:52
…ride

touch_has_app_subscribers() derived its answer from the total subscriber
count minus the backlight subscription, which was written when the
backlight was the only kernel-side subscriber. The watchface long-press
hook added a second permanent subscription, making the function always
return true: the event loop then lit the backlight on every touchdown
and skipped the wake-gesture preference, so a single touch woke the
screen even with touch-to-wake set to double tap.

Add touch_set_system_subscribed() so system-internal subscriptions can
explicitly exclude themselves, and mark the watchface long-press hook
with it. Third-party app subscriptions keep the touchdown-driven
backlight behavior unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Eric Migicovsky <ericmigi@gmail.com>
@ericmigi
ericmigi force-pushed the watchface-longpress-selector branch from 5c8e234 to ee74fa3 Compare July 28, 2026 23:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant