Gaze first: precision and drag buttons, key combinations, pointer role

Two new actions for mouse buttons, controller buttons, and key
combinations: gaze_precision (hold: the pointer stops where you look and
the button's device steers it, a controller by where it points at
POINTER_PRECISION_GAIN, the mouse by its moves; release: click there) and
gaze_drag (the same with a real press at once, dragging until the
release). The relay sends "precision|gazedrag <source> 1|0" to the
helper, which runs them through the same holds as pinches and grips.

- Key combinations on any keyboard ("key_bindings" in the rules, e.g.
  Ctrl+Alt+G for gaze on/off): the last key isn't typed.
- POINTER_GAZE_MOUSE: in gaze mode the left button is a precision button
  (the default, as before) or clicks right away (direct).
- In gaze mode a moving controller no longer takes the pointer away.
- POINTER_ROLE (right, left, stylus): the driver takes a "role" command,
  so the pointer can stay off the hand holding the precision controller,
  which would otherwise take the role back. Needs the rebuilt driver.
- Input Settings: the actions on the Controllers and Buttons pages; on the
  Gaze page the mouse choice, the role, the precision sliders, and key
  combinations (captured from any keyboard).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DeeJanuzandClaude Opus 5.5 committed 2026-09-30 15:14:57 -06:00
1 parent c2739cb0df
commit d666fa031f
6 files changed
+442 -63

No files matched your search

+16
View File
@@ -17,6 +17,11 @@
// btn <name> <0|1> name: trigger, b, x, system, joystick, a (a = claim the laser, no click)
// scroll <x> <y> joystick deflection -1..1
// show | hide connect (take the hand role) or disconnect (give it back)
// role right|left|stylus which role to hint while connected, from now on (the helper
// sends POINTER_ROLE). A Frame controller in your hand takes
// its hand's role back (it counts as used while held), so a
// controller used beside the pointer needs the pointer on the
// other hand, or on no hand at all
//
// The device starts disconnected, so it never holds a hand role at boot (holding
// the right hand while SteamVR started left the Steam UI stuck loading). It
@@ -58,6 +63,7 @@ struct State {
float yaw = 0.f, pitch = 0.f; // degrees
bool buttons[6] = {};
int32_t role = 0; // "role": the hint while connected; 0 = the configured one
float distance = 1.5f;
float scrollX = 0.f, scrollY = 0.f;
};
@@ -173,6 +179,7 @@ public:
std::memcpy(snapshot.buttons, state_->buttons, sizeof snapshot.buttons);
snapshot.scrollX = state_->scrollX;
snapshot.scrollY = state_->scrollY;
snapshot.role = state_->role;
}
DriverPose_t pose{};
@@ -202,6 +209,10 @@ public:
pose.qRotation = QuatFromYawPitch(float(std::atan2(-d[0], -d[2]) * 180.0 / M_PI),
float(std::atan2(d[1], horizontal) * 180.0 / M_PI));
}
if (snapshot.role > 0 && snapshot.role != role_) {
role_ = snapshot.role;
if (hinted_) VRProperties()->SetInt32Property(container_, Prop_ControllerRoleHint_Int32, role_);
}
if (snapshot.visible != hinted_) {
// Claim the hand before connecting; give it up when disconnecting.
VRProperties()->SetInt32Property(container_, Prop_ControllerRoleHint_Int32,
@@ -328,6 +339,11 @@ private:
state_.visible = false;
} else if (std::strncmp(cmd, "show", 4) == 0) {
state_.visible = true;
} else if (std::sscanf(cmd, "role %31s", name) == 1) {
state_.role = !std::strcmp(name, "left") ? TrackedControllerRole_LeftHand
: !std::strcmp(name, "right") ? TrackedControllerRole_RightHand
: !std::strcmp(name, "stylus") ? TrackedControllerRole_Stylus
: state_.role;
} else if (std::sscanf(cmd, "btn %31s %d", name, &v) == 2) {
for (int i = 0; i < kButtons; ++i)
if (std::strcmp(name, kButtonNames[i]) == 0) state_.buttons[i] = v != 0;