Files
DeeJanuz--frametop/hands/include/fh_gestures.h
T
DeeJanuzandClaude Opus 5.5 50c14545ed Hands: pick the cameras by the light, and detect grips
ft-hands tracks with the mono IR cameras in dim light and with every
camera (or the colour pair, HANDS_BRIGHT) in bright light, going by the
colour frames' mean brightness with hysteresis and a 2 s hold
(HANDS_CAMERAS=auto, the default; mono, color and all fix it). Colour
frames are placed on the mono cameras' clock by their dequeue time, and a
view in a camera a step lacks waits for that camera's next frame.

A grip (a closed hand) is a second gesture next to the pinch, in version 2
of the gestures file: every fingertip curled toward the wrist, beginning
only on a hand seen open within a second and held up in front. On the
2026-09-30 lit recording that leaves 6 false grips of 14, all with the
hands on the desk; pinch counts are unchanged. ft-handreplay logs grips
and finger curl, watch_gestures.py shows them, and tools/cut_sets.py
copies a few sets out of a recording.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 14:31:17 -06:00

81 lines
4.5 KiB
C

/*
* fh_gestures - hand gestures ft-hands publishes for input (the pointer helper): look at
* something and pinch to click it, or close the hand (a grip) to press and drag it (the
* Vision Pro model, with the eye tracker doing the looking).
* /run/user/UID/frametop-hands/gestures, next to the hands
* file, with the same sequence lock (read seq, copy, read seq again; use the copy only if
* both reads are the same even number) and the same frame: metres in the head frame at
* capture time, OpenVR's HMD frame (+x right, +y up, -z forward).
*
* One slot per side and gesture: pinch[0] and grip[0] are the left hand, [1] the right.
* A gesture follows the hand it began on until it ends.
* Pinch: begins when the thumb and index tips close within begin_m and ends when they
* open past end_m (the gap between keeps it from flickering). point is between the tips.
* Grip: a closed hand. It begins when all four fingers are curled in (each fingertip
* nearer the wrist than grip_begin times its knuckle is) and ends when they open past
* grip_end on average. distance is that average (about 2 open, under 1.2 closed), strength
* 0 open .. 1 closed, and point the palm's centre. A grip ends a pinch on the same hand
* (closing the hand can pass through a pinch on the way), as lost.
* Either ends, as lost (FH_PINCH_LOST), when its hand stays lost too long.
*
* Don't miss short gestures: a reader that polls slower than a quick tap still sees it,
* because begins and ends count every one. When begins changed, one began at begin_ns;
* when ends changed, one ended at end_ns. begins - ends is 1 while it's down.
*
* Drags: point is where the gesture is now, begin_point where it began. Turn each into the
* room with the HMD pose at its capture time (capture_ns, begin_ns) before subtracting,
* so turning your head doesn't drag.
*
* Version 1 had only the pinches (192 bytes); version 2 adds the grips after them.
*/
#pragma once
#include <assert.h>
#include <stdint.h>
#define FH_GESTURES_MAGIC "FHGEST01"
#define FH_GESTURES_VERSION 2
enum {
FH_PINCH_TRACKED = 1u << 0, /* the hand was tracked in this frame */
FH_PINCH_DOWN = 1u << 1, /* the gesture is held now */
FH_PINCH_LOST = 1u << 2, /* the last one ended because the hand was lost */
/* (or, for a pinch, a grip took over) */
};
typedef struct {
uint32_t flags; /* FH_PINCH_* */
uint32_t hand_id; /* fh_hand_t.id of the hand, 0 if none */
uint32_t begins; /* begun so far */
uint32_t ends; /* ended so far */
uint64_t begin_ns; /* capture time (CLOCK_MONOTONIC) the current or */
/* last one began */
uint64_t end_ns; /* ... the last one ended */
float distance; /* pinch: thumb tip to index tip, m, at this user's */
/* hand size. grip: the fingers' mean curl (above) */
float strength; /* 0 open .. 1 closed */
float point[3]; /* pinch: between the thumb and index tips; grip: */
/* the palm's centre */
float begin_point[3]; /* point when the current or last one began */
} fh_pinch_t; /* 64 bytes */
typedef struct {
char magic[8];
uint32_t version;
uint32_t size;
volatile uint64_t seq;
uint64_t capture_ns; /* CLOCK_MONOTONIC when the cameras took the frames */
uint64_t publish_ns; /* CLOCK_MONOTONIC when this was written */
float begin_m; /* the pinch thresholds in use */
float end_m;
float grip_begin; /* the grip thresholds in use (curl ratios) */
float grip_end;
uint8_t reserved[8];
fh_pinch_t pinch[2]; /* [0] left hand, [1] right hand */
fh_pinch_t grip[2]; /* version 2 */
} fh_gestures_t;
static_assert(sizeof(fh_pinch_t) == 64, "fh_pinch_t layout");
static_assert(sizeof(fh_gestures_t) == 64 + 4 * 64, "fh_gestures_t layout");