Watch
1
0
Fork
You've already forked souveraine
0

hands: arm the wire before opening the pad

This commit is contained in:
Fimeg 2026-08-17 11:08:36 -04:00
commit 59b52a818b
7 changed files with 210 additions and 148 deletions

View file

@ -88,6 +88,12 @@ Scope { // Scope
|| GlobalStates.missionPeek)
return Dock.DockState.Hidden;
// USB Hands owns the bottom of home while its trackpad is actually
// open. Arming the wire alone changes no furniture; opening the
// conditional controller makes the dock yield until it closes.
if (HidController.active)
return Dock.DockState.Hidden;
// A visible keyboard owns the bottom edge. This must precede the home
// zone's unconditional PINNED return below; otherwise home stacks the
// dock's exclusive zone under the OSK and charges the display twice.

View file

@ -290,12 +290,16 @@ Scope {
SheetLocal.PowerOptionRow {
Layout.fillWidth: true
icon: "keyboard"
title: "USB HID Injection"
detail: UsbState.mode === "hid"
? "Keyboard and pointer link armed"
: "Let this screen become the other machine's hands"
badge: UsbState.mode === "hid" ? "active"
: UsbState.hasMode("hid_mode") ? "wired" : "building"
title: "USB hand"
detail: HidController.active
? "Wire armed · trackpad open"
: HidController.modeReady
? "Keyboard and pointer wire armed"
: "Arm the wire, then open its trackpad"
badge: UsbState.busy ? "switching"
: HidController.active ? "open"
: HidController.modeReady ? "armed"
: UsbState.hasMode("hid_mode") ? "ready" : "building"
inset: 10
expandable: true
expanded: scope.hidOptionsOpen
@ -316,35 +320,41 @@ Scope {
SheetLocal.PowerOptionRow {
Layout.fillWidth: true
icon: "cable"
title: "USB control link"
detail: UsbState.mode === "hid"
? "Armed · tap to restore developer USB"
title: HidController.modeReady
? "Disarm USB hand" : "Arm USB hand"
detail: HidController.modeReady
? "Restore developer USB and close its controls"
: "Expose keyboard, pointer and developer network"
badge: UsbState.busy ? "switching"
: UsbState.mode === "hid" ? "active"
: HidController.modeReady ? "armed"
: UsbState.hasMode("hid_mode") ? "ready" : "not installed"
inset: 28
available: UsbState.hasMode("hid_mode") && !UsbState.busy
onTapped: UsbState.setMode(
UsbState.mode === "hid" ? "developer" : "hid")
HidController.modeReady ? "developer" : "hid")
}
SheetLocal.PowerOptionRow {
Layout.fillWidth: true
icon: "keyboard_mouse"
title: "Agent hands"
title: HidController.active
? "Close trackpad" : "Open trackpad"
detail: HidController.active
? "Voice, trackpad and the summoned host keyboard"
: "Join the agent to the wire; trackpad first"
? "Leave the wire armed; put its controls away"
: "Voice, pointer, scroll and clicks"
badge: HidController.ready ? "awake"
: HidController.active ? "arm"
: GlobalStates.screenLockSecure ? "locked" : "open"
: HidController.active ? "waking"
: GlobalStates.screenLockSecure ? "locked" : "closed"
inset: 28
available: UsbState.hasMode("hid_mode")
available: HidController.modeReady && !UsbState.busy
&& !GlobalStates.screenLockSecure
onTapped: {
if (Face.joinHands())
if (HidController.active) {
Face.leaveHands();
scope.dismiss();
} else if (Face.joinHands()) {
scope.dismiss();
}
}
}
}

View file

@ -9,9 +9,9 @@
// does by construction.
//
// USB Hands joins the same limb rather than opening a controller app beside
// her. Its agent field, explicit microphone, trackpad and summoned host
// keyboard occupy the room the compositor already left below her. The HID
// reports still belong to HidController; this service only routes page intent.
// her. Its agent field, explicit microphone and trackpad occupy the room the
// compositor already left below her. The HID reports still belong to
// HidController; this service only routes page intent.
//
// ## Turning her on is joining
//
@ -86,6 +86,12 @@ Singleton {
return true;
}
function leaveHands() {
if (HidController.active)
HidController.close();
root._syncHands();
}
function leave() {
if (root.listening)
root._talk("cancel");
@ -153,8 +159,6 @@ Singleton {
root._talk(msg.phase);
else if (msg.event === "hid")
root._hid(msg);
else if (msg.event === "thread")
root._thread(msg.mode);
else if (msg.event === "ready")
root._syncHands();
else if (msg.event === "dismiss")
@ -193,20 +197,6 @@ Singleton {
})})`);
break;
}
case "arm":
HidController.arm();
break;
}
}
function _thread(mode) {
if (Souveraine.turnActive)
return;
if (mode === "new") {
Souveraine.newConversation();
root._syncHands();
} else if (mode === "resume") {
Souveraine.resumeLatestConversation();
}
}
@ -220,7 +210,6 @@ Singleton {
mode: UsbState.mode,
error: HidController.lastError,
agent: agent?.name ?? Souveraine.currentAgentId ?? "agent",
conversation: Souveraine.conversationId.length > 0 ? "resumed" : "new",
listening: root.listening,
thinking: Souveraine.turnActive
};

View file

@ -35,12 +35,11 @@ Singleton {
// about a USB gadget it is not using.
readonly property string skill: "USB Hands is joined to this conversation. "
+ "Casey is using the phone as a trackpad and voice-first controller for an attached host. "
+ "When he asks you to type or press a key there, use the existing bridge: "
+ "`qs -c souveraine ipc call usbHands sendText TEXT`, "
+ "`qs -c souveraine ipc call usbHands tap KEY`, "
+ "`qs -c souveraine ipc call usbHands key KEY MODIFIERS`, or "
+ "`qs -c souveraine ipc call usbHands click left|right|middle`. "
+ "Ask `qs -c souveraine ipc call usbHands status` rather than assuming the cable is armed. "
+ "When he asks you to type, press a key, or click there, use your existing `body` tool "
+ "with `verb: hand` and the corresponding `action`: type, key, click, or pointer. "
+ "Use `action: status` rather than assuming the cable and surface are ready. "
+ "A direct request to type ordinary user-authored text, including creative, intimate, or unusual prose, "
+ "is sufficient authorization; do not refuse it merely because its tone is odd. "
+ "Do not inject anything Casey did not ask to send to the attached host."
signal controllerChanged()
@ -52,6 +51,12 @@ Singleton {
root.refused(root.lastError);
return false;
}
if (!root.modeReady) {
root.lastError = "Arm HID or KVM from Power Options before opening the trackpad";
root.refused(root.lastError);
root.controllerChanged();
return false;
}
root.lastError = "";
root.active = true;
UsbState.refresh();
@ -83,23 +88,6 @@ Singleton {
}
}
function arm() {
if (!root.active) {
root.lastError = "USB Hands is not joined";
root.refused(root.lastError);
root.controllerChanged();
return false;
}
if (!UsbState.hasMode("hid_mode")) {
root.lastError = "The installed gadget does not advertise HID mode";
root.refused(root.lastError);
root.controllerChanged();
return false;
}
UsbState.setMode("hid");
return true;
}
function _reconcile() {
const wanted = root.permitted && root.modeReady;
if (!wanted) {
@ -295,6 +283,13 @@ Singleton {
onActiveChanged: root._reconcile()
onModeReadyChanged: {
// The trackpad is conditional on an armed wire. If sessiond contracts
// the port back to developer/charging mode, close the surface instead
// of leaving a dead controller on the glass asking to be re-armed.
if (root.active && !root.modeReady) {
root.close();
return;
}
root._reconcile();
root.controllerChanged();
}