Add Remote controller capture IPC to the host and both SDKs
remote.focus lets a controller keyboard-capture lease ask core to enter or leave remote focus; the reply acknowledges admission only. Core reports the actual state with remote.focus events and forwards routed transitions as remote.input events carrying the source keycode and, on fresh presses, a canonical key name. A full focus mailbox revokes the lease; revocation drains pending focus work. No native capture owner is attached yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
27017bef11
commit
2d5327adf4
10 files changed
+583
-10
No files matched your search
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package tentaclewire
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRemoteCapturedKeyValidation(t *testing.T) {
|
||||
for _, key := range []RemoteCapturedKey{
|
||||
{Code: 8, Name: "a", Pressed: true}, {Code: 255, Name: "Shift_R", Pressed: true},
|
||||
{Code: 38, Pressed: true, Repeat: true}, {Code: 38},
|
||||
} {
|
||||
if !ValidRemoteCapturedKey(key) {
|
||||
t.Fatal("valid key rejected", key)
|
||||
}
|
||||
data, _ := json.Marshal(key)
|
||||
var decoded RemoteCapturedKey
|
||||
if json.Unmarshal(data, &decoded) != nil || decoded != key {
|
||||
t.Fatal("round trip changed key", key)
|
||||
}
|
||||
}
|
||||
for _, raw := range []string{
|
||||
`{"code":7,"name":"a","pressed":true}`, `{"code":256,"name":"a","pressed":true}`,
|
||||
`{"code":38,"name":"Prior","pressed":true}`, `{"code":38,"name":"a","pressed":false}`,
|
||||
`{"code":38,"pressed":false,"repeat":true}`, `{"code":38,"pressed":true}`,
|
||||
`{"name":"a","pressed":true}`, `{"code":38,"name":"a"}`,
|
||||
`{"code":38,"name":"a","pressed":true,"origin":"local"}`, `{"code":38,"code":39,"name":"a","pressed":true}`,
|
||||
} {
|
||||
var key RemoteCapturedKey
|
||||
if json.Unmarshal([]byte(raw), &key) == nil {
|
||||
t.Fatal("invalid key decoded", raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteFocusRequestAndEventValidation(t *testing.T) {
|
||||
lease := strings.Repeat("a", 32)
|
||||
var request RemoteFocusRequest
|
||||
if json.Unmarshal([]byte(`{"lease":"`+lease+`","epoch":1,"sequence":2,"remote":false}`), &request) != nil || request.Remote || request.Sequence != 2 {
|
||||
t.Fatal("valid focus request rejected", request)
|
||||
}
|
||||
for _, raw := range []string{
|
||||
`{"lease":"` + lease + `","epoch":1,"sequence":2}`,
|
||||
`{"lease":"` + lease + `","epoch":1,"sequence":0,"remote":true}`,
|
||||
`{"lease":"short","epoch":1,"sequence":2,"remote":true}`,
|
||||
`{"lease":"` + lease + `","epoch":1,"sequence":2,"remote":"yes"}`,
|
||||
} {
|
||||
if json.Unmarshal([]byte(raw), &request) == nil {
|
||||
t.Fatal("invalid focus request decoded", raw)
|
||||
}
|
||||
}
|
||||
key := RemoteCapturedKey{Code: 38, Name: "a", Pressed: true}
|
||||
valid := []SessionEvent{
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.input", Lease: lease, Input: &key},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "remote", Reason: "requested"},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "local", Reason: "escape"},
|
||||
}
|
||||
for _, event := range valid {
|
||||
if !ValidSessionEvent(event) {
|
||||
t.Fatal("valid event rejected", event)
|
||||
}
|
||||
}
|
||||
invalid := []SessionEvent{
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.input", Lease: lease},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.input", Lease: lease, Input: &key, Focus: "remote"},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "remote", Reason: "network"},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "elsewhere", Reason: "requested"},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.revoked", Lease: lease, Reason: "closed", Input: &key},
|
||||
{Sequence: 1, Epoch: 1, Kind: "remote.revoked", Lease: lease, Reason: "closed", Focus: "local"},
|
||||
}
|
||||
for _, event := range invalid {
|
||||
if ValidSessionEvent(event) {
|
||||
t.Fatal("invalid event accepted", event)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user