even-realities/everything-evenhub

handle-input

Handle user input and events in Even Hub G2 apps — touchpad gestures, ring input, scroll, foreground/background lifecycle, and event routing.

ソースを見る
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

Handle Input

Guide for handling user input, gestures, and lifecycle events in Even Hub G2 apps.

Input Sources

SourceGesturesNotes
G2 touchpads (temple)Press, double press, swipe up, swipe downPrimary input
R1 touchpads (ring)Press, double press, swipe up, swipe downOptional accessory, same gesture set
IMU (accelerometer/gyroscope)Head orientation, motion dataSee device-features skill

Event Routing Rules

Events route differently depending on the active container type (the one with isEventCapture: 1). Only one container per page can capture events.

Text container active

GestureEvent fieldeventType
Swipe upevent.textEvent1 (SCROLLTOPEVENT)
Swipe downevent.textEvent2 (SCROLLBOTTOMEVENT)
Single pressevent.sysEventundefined / 0
Double pressevent.sysEvent3 (DOUBLECLICKEVENT)

Key point: clicks and double-clicks on text containers fire as sysEvent, NOT textEvent. Only scroll gestures fire as textEvent.

List container active

GestureEvent fieldDetails
Swipe up/down(internal)SDK scrolls the list internally, no event fired
Single pressevent.listEvent.currentSelectItemIndex = selected item index
Double pressevent.sysEvent.eventType = 3

System events (always available)

GestureEvent fieldeventType
Foreground enterevent.sysEvent4 (FOREGROUNDENTEREVENT)
Foreground exitevent.sysEvent5 (FOREGROUNDEXITEVENT)
Abnormal exitevent.sysEvent6 (ABNORMALEXITEVENT)
System exitevent.sysEvent7 (SYSTEMEXITEVENT)

OsEventTypeList enum values

ValueNameDescription
0CLICK_EVENTSingle press (G2 or R1)
1SCROLLTOPEVENTSwipe up
2SCROLLBOTTOMEVENTSwipe down
3DOUBLECLICKEVENTDouble press (G2 or R1)
4FOREGROUNDENTEREVENTApp comes to foreground
5FOREGROUNDEXITEVENTApp goes to background
6ABNORMALEXITEVENTUnexpected disconnect
7SYSTEMEXITEVENTSystem-level exit (e.g. user confirmed exit dialog)
8IMUDATAREPORTIMU data sample

Event Models

typescript
interface Text_ItemEvent {
  containerID?: number
  containerName?: string
  eventType?: number  // 1 = scroll up, 2 = scroll down
}

interface List_ItemEvent {
  containerID?: number
  containerName?: string
  currentSelectItemName?: string
  currentSelectItemIndex?: number  // 0-based; undefined when 0 (protobuf)
  eventType?: number
}

interface Sys_ItemEvent {
  eventType?: number    // undefined/0 = single click, 3 = double click
  eventSource?: EventSourceType  // 0=null, 1=glasses right, 2=ring, 3=glasses left
  imuData?: IMU_Report_Data
  systemExitReasonCode?: number
}

Protobuf Zero-Value Omission

The SDK uses protobuf under the hood. Any field with a zero/default value (0, false, empty string) will be `undefined`, not the zero value. This affects:

  • sysEvent.eventType — single click is 0, but arrives as undefined
  • listEvent.currentSelectItemIndex — first item is 0, but arrives as undefined
  • Any other numeric field with value 0

Always use nullish coalescing: event.sysEvent.eventType ?? 0, event.listEvent.currentSelectItemIndex ?? 0.

Complete Event Handling Template

typescript
import { waitForEvenAppBridge } from '@evenrealities/even_hub_sdk'

const bridge = await waitForEvenAppBridge()

const unsubscribe = bridge.onEvenHubEvent(event => {
  if (event.listEvent) {
    // List item selected (single press on list container)
    const idx = event.listEvent.currentSelectItemIndex ?? 0
    console.log('Selected index:', idx)
    return
  }

  if (event.textEvent) {
    // Scroll on text container (NOT clicks — those come via sysEvent)
    const type = event.textEvent.eventType ?? 0
    if (type === 1) {
      // Swipe up / scroll up
    } else if (type === 2) {
      // Swipe down / scroll down
    }
    return
  }

  if (event.sysEvent) {
    const type = event.sysEvent.eventType ?? 0
    if (type === 0) {
      // Single press (on text container, or system-level)
    } else if (type === 3) {
      // Double press
    } else if (type === 4) {
      // App resumed (foreground enter)
    } else if (type === 5) {
      // App backgrounded (foreground exit)
    }
    return
  }
})

// Always clean up on teardown
// unsubscribe()

G2 vs R1 Distinction

G2 (temple touchpads) and R1 (ring touchpads) share the same gesture set. To distinguish between them, check eventSource in Sys_ItemEvent. The EventSourceType value indicates whether input came from the left arm, right arm, or ring accessory.

Exit Mechanism

Every app should provide a way to exit via glasses/ring interaction. Use the SDK's built-in system exit dialog rather than building your own confirmation UI.

Canonical pattern — double-tap to show system exit dialog:

typescript
if (eventType === 3) { // DOUBLE_CLICK_EVENT
  // Show the system exit dialog. Don't clean up resources here —
  // the user can still cancel. If they confirm, the SDK fires
  // SYSTEM_EXIT_EVENT (7) and you clean up in that handler.
  bridge.shutDownPageContainer(1)
  return
}

`shutDownPageContainer` modes:

  • shutDownPageContainer(0) — immediate exit, no confirmation
  • shutDownPageContainer(1) — system exit confirmation dialog (recommended)

Do not unsubscribe() / stop hardware / flush state before calling shutDownPageContainer(1). If you do and the user taps cancel, the app is still on screen but no longer listening for events. Clean up in the ABNORMAL_EXIT_EVENT / SYSTEM_EXIT_EVENT handlers instead.

Lifecycle Events

Handle all four lifecycle events for a clean app:

EventWhen to use it
FOREGROUND_ENTER_EVENT (4)Re-render current state, resume timers/IMU
FOREGROUND_EXIT_EVENT (5)Flush pending state to setLocalStorage, pause timers
ABNORMAL_EXIT_EVENT (6)Stop hardware (imuControl(false), audioControl(false)), unsubscribe, flush state
SYSTEM_EXIT_EVENT (7)Same cleanup as ABNORMAL_EXIT — user confirmed exit from the system dialog

Important Notes

  • Clicks on text containers route to `sysEvent`, not textEvent. Only scroll gestures fire textEvent. This is the most common source of event-handling bugs.
  • Cleanup: bridge.onEvenHubEvent() returns an unsubscribe function. Always call it on component teardown.
  • One event listener per page: Only the container with isEventCapture: 1 receives input events. If multiple containers have isEventCapture: 1, the SDK rejects the page with a validation error.

Task

$ARGUMENTS

同じリポジトリから

関連する Skills

すべての Skills