diff --git a/docs/tentacle-remote-keyboard.md b/docs/tentacle-remote-keyboard.md index 9883819..a004bd2 100644 --- a/docs/tentacle-remote-keyboard.md +++ b/docs/tentacle-remote-keyboard.md @@ -29,6 +29,12 @@ allowlist matches the external mapper's canonical subset: - Left/right Shift, Control, Alt and Super; Menu, lock keys, Pause and Print. - Backspace, Tab, Return, Escape and Space; main navigation, Insert and Delete. - Keypad Enter, navigation, Begin, Insert, Delete and arithmetic/separator keys. +- Eighteen browser, volume, media and launch keys (`XF86Back`, `XF86Forward`, + `XF86Reload`, `XF86Stop`, `XF86Search`, `XF86Favorites`, `XF86HomePage`, + `XF86AudioMute`, `XF86AudioLowerVolume`, `XF86AudioRaiseVolume`, + `XF86AudioNext`, `XF86AudioPrev`, `XF86AudioStop`, `XF86AudioPlay`, + `XF86Mail`, `XF86AudioMedia`, `XF86MyComputer`, `XF86Calculator`), matching + Windows VK 0xA6–0xB7. Other XF86 keys, such as power and eject, stay unsupported. - The eleven explicit punctuation identities supported by the external US profile. The owner must resolve the destination layout; this does not grant permission to guess a matching physical key or change the global keymap. diff --git a/internal/app/tentacle_remote_connection_test.go b/internal/app/tentacle_remote_connection_test.go index c5370fb..e7b9ffa 100644 --- a/internal/app/tentacle_remote_connection_test.go +++ b/internal/app/tentacle_remote_connection_test.go @@ -116,7 +116,9 @@ func select_remote_relay(t *testing.T, forms *consentTestForms, address, role st if element.ID == "channel-key" && (!element.Secret || element.Text != "") { t.Fatal("key unmasked or prefilled") } - if element.Kind == interaction.CheckBox { + // The only Tentacle checkbox is remembering the host and key; + // access is chosen solely in the reader-owned approval form. + if element.Kind == interaction.CheckBox && element.ID != "remember" { t.Fatal("permissions duplicated") } } diff --git a/internal/input/remote_capture_test.go b/internal/input/remote_capture_test.go index 1269ca4..2622f6c 100644 --- a/internal/input/remote_capture_test.go +++ b/internal/input/remote_capture_test.go @@ -315,7 +315,7 @@ func TestBehaviorRemoteCaptureConsumesUnsupportedKeys(t *testing.T) { {release(121), RemoteCaptureResult{}}, {release(codeShift), remote(up(codeShift))}, }) - for keysym, want := range map[string]string{"Prior": "Page_Up", "KP_Next": "KP_Page_Down", "a": "a", "A": "", "XF86AudioMute": "", "ISO_Level3_Shift": ""} { + for keysym, want := range map[string]string{"Prior": "Page_Up", "KP_Next": "KP_Page_Down", "a": "a", "A": "", "XF86AudioMute": "XF86AudioMute", "XF86Eject": "", "ISO_Level3_Shift": ""} { if got := RemoteCaptureKeyName(keysym); got != want { t.Fatalf("%s = %q, want %q", keysym, got, want) } diff --git a/internal/input/remote_key_names.go b/internal/input/remote_key_names.go index 4b822ec..b56b498 100644 --- a/internal/input/remote_key_names.go +++ b/internal/input/remote_key_names.go @@ -28,7 +28,12 @@ func ValidRemoteKeyName(name string) bool { "KP_Left", "KP_Up", "KP_Right", "KP_Down", "KP_Insert", "KP_Delete", "KP_Multiply", "KP_Add", "KP_Separator", "KP_Subtract", "KP_Decimal", "KP_Divide", "semicolon", "equal", "comma", "minus", "period", "slash", "grave", "bracketleft", - "backslash", "bracketright", "apostrophe": + "backslash", "bracketright", "apostrophe", + // Browser, volume, media and launch keys (Windows VK 0xa6-0xb7). + "XF86Back", "XF86Forward", "XF86Reload", "XF86Stop", "XF86Search", "XF86Favorites", + "XF86HomePage", "XF86AudioMute", "XF86AudioLowerVolume", "XF86AudioRaiseVolume", + "XF86AudioNext", "XF86AudioPrev", "XF86AudioStop", "XF86AudioPlay", "XF86Mail", + "XF86AudioMedia", "XF86MyComputer", "XF86Calculator": return true } return false diff --git a/internal/platform/xorg/remote_capture_native_test.go b/internal/platform/xorg/remote_capture_native_test.go index 4383a98..282ee03 100644 --- a/internal/platform/xorg/remote_capture_native_test.go +++ b/internal/platform/xorg/remote_capture_native_test.go @@ -113,8 +113,13 @@ func TestNativeRemoteCaptureForwardsBypassesAndEscapes(t *testing.T) { expect("t", true, true) expect("", false, false) expect("", false, false) - // Unsupported keys are consumed locally, not forwarded. + // Media keys are forwarded. run("key", "XF86AudioMute") + if mute := expect("XF86AudioMute", true, true); expect("", false, false) != mute { + t.Fatal("media key release paired with another keycode") + } + // Unsupported keys are consumed locally, not forwarded. + run("key", "XF86Eject") // Remote's own shortcut stays local: it withdraws the forwarded prefix and // returns the keyboard here without running the command. run("key", "Insert+ctrl+r") diff --git a/internal/tentaclewire/remote_key_test.go b/internal/tentaclewire/remote_key_test.go index 53227ae..a95e5ab 100644 --- a/internal/tentaclewire/remote_key_test.go +++ b/internal/tentaclewire/remote_key_test.go @@ -22,6 +22,10 @@ func TestRemoteKeySDKValidation(t *testing.T) { {`{"name":"Control_R","pressed":false}`, true}, {`{"name":"KP_Enter","pressed":true}`, true}, {`{"name":"semicolon","pressed":false}`, true}, + {`{"name":"XF86AudioRaiseVolume","pressed":true}`, true}, + {`{"name":"XF86Calculator","pressed":false}`, true}, + {`{"name":"XF86Eject","pressed":true}`, false}, + {`{"name":"XF86PowerOff","pressed":true}`, false}, {`{"name":"Prior","pressed":true}`, false}, {`{"name":"A","pressed":true}`, false}, {`{"name":"F01","pressed":true}`, false}, diff --git a/sdk/python/cthulhu_tentacle/session.py b/sdk/python/cthulhu_tentacle/session.py index 414872f..f49692b 100644 --- a/sdk/python/cthulhu_tentacle/session.py +++ b/sdk/python/cthulhu_tentacle/session.py @@ -126,7 +126,12 @@ def valid_remote_key(key): "KP_Left", "KP_Up", "KP_Right", "KP_Down", "KP_Insert", "KP_Delete", "KP_Multiply", "KP_Add", "KP_Separator", "KP_Subtract", "KP_Decimal", "KP_Divide", "semicolon", "equal", "comma", "minus", "period", "slash", "grave", "bracketleft", - "backslash", "bracketright", "apostrophe") + "backslash", "bracketright", "apostrophe", + # Browser, volume, media and launch keys (Windows VK 0xa6-0xb7). + "XF86Back", "XF86Forward", "XF86Reload", "XF86Stop", "XF86Search", "XF86Favorites", + "XF86HomePage", "XF86AudioMute", "XF86AudioLowerVolume", "XF86AudioRaiseVolume", + "XF86AudioNext", "XF86AudioPrev", "XF86AudioStop", "XF86AudioPlay", "XF86Mail", + "XF86AudioMedia", "XF86MyComputer", "XF86Calculator") except (ProtocolError, TypeError, ValueError): return False