services/touch: synthesize button clicks from touch, and route action-bar taps#1785
Open
tsutomu1984-hub wants to merge 8 commits into
Open
services/touch: synthesize button clicks from touch, and route action-bar taps#1785tsutomu1984-hub wants to merge 8 commits into
tsutomu1984-hub wants to merge 8 commits into
Conversation
Investigation-only test (no synthesis bridge is implemented) for a possible future touch->button "synthesis bridge" that would let apps using their own click handlers respond to touch. It models the proposed suppression decision -- synthesize a button only when the focused app is not a direct touch subscriber -- against the real touch_has_app_subscribers() bookkeeping plus a dummy touch-consuming app, and asserts that a single touch never produces a double action. It also makes the async-subscription timing window observable: with a persistent-sensor bridge, a touch in the window between an app calling subscribe() and KernelMain processing it would be wrongly synthesized, whereas the coupled-sensor design is race-free. Kept on experimental/touch-click-synthesis for a separate future PR; the synthesis bridge itself is intentionally not implemented yet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Add touch_set_synthesis_enabled(), a kernel-internal holder of the touch sensor for the upcoming touch->click synthesis bridge, mirroring the backlight subscription. Like the backlight holder it powers the sensor but is excluded from touch_has_app_subscribers(), so it never counts as a real app subscriber. Also add the CONFIG_TOUCH_CLICK_SYNTHESIS Kconfig option (default n) that will gate the bridge, kept separate from CONFIG_TOUCH so the feature can be disabled independently of the touch driver. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Add a kernel-level bridge (CONFIG_TOUCH_CLICK_SYNTHESIS) that turns a touch tap into a SELECT button click, so apps that use their own click handlers instead of ScrollLayer/MenuLayer respond to touch. The bridge is active only while a watchapp is the focused foreground process (gated on PEBBLE_APP_DID_CHANGE_FOCUS_EVENT), giving it the same "on while viewing an app" sensor power profile as the widget-level touch support. Tap detection reuses ScrollLayer's definition: a liftoff within 300ms that never moved past 10px. On a tap it posts a SELECT button down+up through the normal button path, and it suppresses itself while the focused app consumes touch directly (touch_has_app_subscribers()) so there is no double action. Phase 1 handles tap->SELECT only; swipe->UP/DOWN/BACK is a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Extend the touch->click synthesis bridge so a vertical swipe (a drag past the 10px threshold whose net movement is predominantly vertical) synthesizes an UP or DOWN button click, alongside the existing tap->SELECT. Horizontal swipes are ignored for now; BACK is a later phase. The mapping is spatial: a swipe toward the top of the screen is UP, toward the bottom is DOWN. The foreground gate and the touch_has_app_subscribers() arbitration are shared with the tap path, so a swipe is also suppressed while the focused app consumes touch itself. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Extend the touch-to-click bridge so it is active while a focusing modal (notification popup, action menu, dialog, ...) is up, not only while a watchapp is the focused foreground process. A focused modal receives button events in launcher_handle_button_event, so synthesizing for it lets touch drive, e.g., scrolling the notification detail body. The synthesis decision is now a live re-check evaluated at touch time rather than a value cached from the focus event. A focusing modal's did_focus fires only once its entrance transition completes, so a cached gate could miss touches arriving before then; the live re-check is robust to that timing as long as the sensor is held. The focus event still drives the sensor hold. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Invert the vertical-swipe to UP/DOWN mapping so it follows the content-scroll convention used by ScrollLayer/MenuLayer touch and common touch UIs: flicking the finger up (to reveal content further down) synthesizes DOWN, and flicking down synthesizes UP. Previously the button matched the finger's physical travel, which felt reversed when scrolling a notification body. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Add a kernel-visible action bar descriptor {present, global frame, icon
mask} to the touch-to-click synthesis bridge, published from applib via a
new internal syscall (sys_touch_click_synth_set_action_bar). When a tap
lands on the bar, the bridge routes it to UP/SELECT/DOWN by vertical zone
(top/middle/bottom third), falling back to SELECT for a zone whose icon
is absent; taps outside the bar remain SELECT. This lets apps that draw
an ActionBarLayer and use their own click handlers (e.g. the Music app's
volume-up / play-pause / volume-down bar) respond to touch.
Swipes are unchanged (full-screen UP/DOWN). The descriptor is dropped
when the bridge leaves the foreground so it can't route taps in the next
context.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
Publish the action bar's geometry and icon-presence mask to the touch-to-click synthesis bridge on add-to-window, icon change, and remove-from-window, so a tap on the bar is routed to the matching button. All of it compiles out unless CONFIG_TOUCH_CLICK_SYNTHESIS is set, so the app SDK build is unaffected. Also clear the descriptor from the Music app's deinit: it does not otherwise remove its action bar on exit, which would leave a stale descriptor able to route right-edge taps in the next app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: tsutomu sasaki <tsutomu1984@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on: #1784 (touch_service list_node fix — required for correct subscriber-count arbitration).
What
A kernel-side touch-to-click synthesis bridge: it maps touch gestures to the existing button semantics of whatever app/modal is focused, so apps that use their own click handlers (rather than ScrollLayer/MenuLayer) respond to touch:
SELECT(orUP/SELECT/DOWNby vertical zone when the tap lands on anActionBarLayer, e.g. the Music app's volume / play-pause bar)UP/DOWNHow it differs from #1773 (design note)
Unlike #1773, which changes the applib ScrollLayer/MenuLayer widgets that third-party apps link against, this bridge is a kernel-side input mapping with no applib/ABI change. It does not modify any widget; it posts the same
PEBBLE_BUTTON_DOWN/PEBBLE_BUTTON_UPevents a physical press would, which flow through the normal click path. So it introduces no new per-widget behavior and no SDK surface.Gating & arbitration
CONFIG_TOUCH_CLICK_SYNTHESIS(Kconfig, default n) — not compiled into default builds.touch_enabledpref, default on) and the foreground is a non-watchface app or a focused modal.touch_has_app_subscribers()), so it never double-handles apps that already use ScrollLayer/MenuLayer touch (applib: touch drag-to-scroll and tap-to-select for ScrollLayer / MenuLayer #1773) or their own touch subscription.Note on third-party apps (re: the #1773 review discussion)
When the Kconfig is enabled, the bridge does map touch onto the existing button handlers of the focused app, including third-party apps that have no touch handling of their own — giving them touch as an alternate way to trigger their current button actions (never new behavior, and never for apps that already handle touch). There is no per-app / system-app-only restriction today. If maintainers prefer limiting synthesis to system apps or adding a dedicated runtime toggle, that's a straightforward follow-up and we're happy to add it.
Testing
Unit tests:
test_touch_click_synth(23) +test_touch_click_arbitration. Verified on-device (Pebble Time 2): tap→SELECT and swipe on Notifications; ActionBar up/select/down taps in Music; no misfires on the watchface.🤖 Generated with Claude Code