Skip to content

Button wrapper makes long-press tracking impossible in JavaScript #12

@vincentezw

Description

@vincentezw

The new Button class wrapper handles physical button interactions by passing raw down/up state events directly to JavaScript. However, because it hooks directly into low-level hardware interrupts (PEBBLE_BUTTON_DOWN_EVENT and PEBBLE_BUTTON_UP_EVENT) without neutralizing PebbleOS's native menu-scrolling auto-repeat logic or configuring window click overrides, a continuous physical hold is delivered to JavaScript as a rapid succession of alternating, independent tap and hold cycles.

This makes it impossible to implement reliable long-press versus short-press logic in JavaScript, as the underlying platform forcefully injects synthetic release/re-press cycles into the stream during a single physical hold.

Steps to Reproduce
Instantiate a basic button handler tracking time duration on release:

let buttonDownTime = null;

new Button({
  types: ["select"],
  onPush(down, type) {
    if (down) {
      if (buttonDownTime) return; 
      buttonDownTime = Date.now();
    } else {
      if (!buttonDownTime) return;
      const duration = Date.now() - buttonDownTime;
      buttonDownTime = null;
      
      if (duration >= 600) {
        console.log("Long Press Detected");
      } else {
        console.log("Short Press Detected");
      }
    }
  }
});

Press and hold down the button continuously and observe the JavaScript execution path. Instead of firing a single long-press event when the button is held down continuously, the execution logs register an initial short press followed by a long press a few seconds later as the raw event stream cycles through multiple phantom release and press states:

[21:47:02] xsHost.c:155> Short Press Detected
[21:47:04] xsHost.c:155> Long Press Detected

Looking at the underlying C bridge configuration:

if (NULL == getModdableAppState(buttons)) {
    EventServiceInfo *i = &getModdableAppState(eventServiceDown);
    i->type = PEBBLE_BUTTON_DOWN_EVENT;
    i->handler = buttonDownEventHandler;
    event_service_client_subscribe(i);

    i = &getModdableAppState(eventServiceUp);
    i->type = PEBBLE_BUTTON_UP_EVENT;
    i->handler = buttonUpEventHandler;
    event_service_client_subscribe(i);
}

The bridge subscribes directly to raw EventService interrupts and mirrors them straight to the XS machine context via xsCallFunction2(xsReference(pb->onPush), pb->obj, xsVar(0), xsVar(1));.

Because PebbleOS handles button interactions natively through its higher-level window click configurations (ClickConfigProvider), exposing these raw, unconfigured event interrupts directly to the JavaScript layer introduces artificial state cycles that break standard JS event timing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions