Handle Input
Guide for handling user input, gestures, and lifecycle events in Even Hub G2 apps.
Input Sources
| Source | Gestures | Notes |
|---|---|---|
| G2 touchpads (temple) | Press, double press, swipe up, swipe down | Primary input |
| R1 touchpads (ring) | Press, double press, swipe up, swipe down | Optional accessory, same gesture set |
| IMU (accelerometer/gyroscope) | Head orientation, motion data | See 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
| Gesture | Event field | eventType |
|---|---|---|
| Swipe up | event.textEvent |
1 (SCROLL_TOP_EVENT) |
| Swipe down | event.textEvent |
2 (SCROLL_BOTTOM_EVENT) |
| Single press | event.sysEvent |
undefined / 0 |
| Double press | event.sysEvent |
3 (DOUBLE_CLICK_EVENT) |
Key point: clicks and double-clicks on text containers fire as sysEvent, NOT textEvent. Only scroll gestures fire as textEvent.
List container active
| Gesture | Event field | Details |
|---|---|---|
| Swipe up/down | (internal) | SDK scrolls the list internally, no event fired |
| Single press | event.listEvent |
.currentSelectItemIndex = selected item index |
| Double press | event.sysEvent |
.eventType = 3 |
System events (always available)
| Gesture | Event field | eventType |
|---|---|---|
| Foreground enter | event.sysEvent |
4 (FOREGROUND_ENTER_EVENT) |
| Foreground exit | event.sysEvent |
5 (FOREGROUND_EXIT_EVENT) |
| Abnormal exit | event.sysEvent |
6 (ABNORMAL_EXIT_EVENT) |
| System exit | event.sysEvent |
7 (SYSTEM_EXIT_EVENT) |
OsEventTypeList enum values
| Value | Name | Description |
|---|---|---|
| 0 | CLICK_EVENT | Single press (G2 or R1) |
| 1 | SCROLL_TOP_EVENT | Swipe up |
| 2 | SCROLL_BOTTOM_EVENT | Swipe down |
| 3 | DOUBLE_CLICK_EVENT | Double press (G2 or R1) |
| 4 | FOREGROUND_ENTER_EVENT | App comes to foreground |
| 5 | FOREGROUND_EXIT_EVENT | App goes to background |
| 6 | ABNORMAL_EXIT_EVENT | Unexpected disconnect |
| 7 | SYSTEM_EXIT_EVENT | System-level exit (e.g. user confirmed exit dialog) |
| 8 | IMU_DATA_REPORT | IMU data sample |
Event Models
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 is0, but arrives asundefinedlistEvent.currentSelectItemIndex— first item is0, but arrives asundefined- Any other numeric field with value
0
Always use nullish coalescing: event.sysEvent.eventType ?? 0, event.listEvent.currentSelectItemIndex ?? 0.
Complete Event Handling Template
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:
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 confirmationshutDownPageContainer(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:
| Event | When 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, nottextEvent. Only scroll gestures firetextEvent. 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: 1receives input events. If multiple containers haveisEventCapture: 1, the SDK rejects the page with a validation error.
Task
$ARGUMENTS