diff --git a/docs/adapters.md b/docs/adapters.md index 86b8541..d4b79b0 100644 --- a/docs/adapters.md +++ b/docs/adapters.md @@ -35,6 +35,21 @@ new metadata fields make backend selection easier for agents and host apps: - `transport`: memory, callback, WebSocket, or native process - `virtualDeviceKind`: none, native helper, or OS virtual gamepad +For `NativeBridgeAdapter` and `NativeProcessBridgeAdapter`, these formats and +feature booleans describe the configured SDK serializer. Their default options +advertise full controller state, touchpad/motion/status data, and profile HID +reports. Disabling `includeState`, `includeExtensions`, and +`includeProfileHidReport` removes those corresponding claims while retaining +the XInput and HID gamepad reports and the JSONL transport. Touchpad and gyro +remain advertised if any enabled state channel can carry them; device status +tracks the full state and extensions channels. +`supportedCommands` continues to describe runtime actions the adapter accepts; +it does not change when an output channel is suppressed. + +This metadata does not establish support from the operating system, browser, +physical device, Steam, or a game. Those layers may omit or reinterpret data +even when the SDK serializer emits it. + ```ts const capabilities = controller.capabilities(); diff --git a/packages/core/src/__tests__/native-bridge-capabilities.test.ts b/packages/core/src/__tests__/native-bridge-capabilities.test.ts new file mode 100644 index 0000000..1b87995 --- /dev/null +++ b/packages/core/src/__tests__/native-bridge-capabilities.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, test } from "bun:test"; +import { NativeBridgeAdapter, NativeProcessBridgeAdapter } from "../adapters"; + +describe("native bridge adapter capabilities", () => { + test("describe default serialized state and profile report outputs", () => { + const adapters = [ + new NativeBridgeAdapter(), + new NativeProcessBridgeAdapter({ command: "/bin/cat" }), + ]; + + for (const adapter of adapters) { + const capabilities = adapter.capabilities(); + expect(capabilities.supportsTouchpad).toBe(true); + expect(capabilities.supportsGyro).toBe(true); + expect(capabilities.supportsDeviceStatus).toBe(true); + expect(capabilities.outputFormats).toEqual([ + "controller-state", + "xinput-report", + "hid-gamepad-report", + "hid-playstation-extended-report", + "hid-switch-extended-report", + "native-bridge-jsonl", + ]); + expect(capabilities.reportFormats).toEqual([ + "xinput", + "hid-gamepad", + "hid-playstation-extended", + "hid-switch-extended", + ]); + } + }); + + test("omit capability claims for fully suppressed payload channels", () => { + const adapters = [ + new NativeBridgeAdapter({ + includeState: false, + includeExtensions: false, + includeProfileHidReport: false, + }), + new NativeProcessBridgeAdapter({ + command: "/bin/cat", + includeState: false, + includeExtensions: false, + includeProfileHidReport: false, + }), + ]; + + for (const adapter of adapters) { + const capabilities = adapter.capabilities(); + expect(capabilities.supportsTouchpad).toBe(false); + expect(capabilities.supportsGyro).toBe(false); + expect(capabilities.supportsDeviceStatus).toBe(false); + expect(capabilities.outputFormats).toEqual([ + "xinput-report", + "hid-gamepad-report", + "native-bridge-jsonl", + ]); + expect(capabilities.reportFormats).toEqual(["xinput", "hid-gamepad"]); + expect(capabilities.supportedCommands).toContain("touchpad"); + expect(capabilities.supportedCommands).toContain("motion"); + } + }); +}); diff --git a/packages/core/src/adapters/native-bridge.ts b/packages/core/src/adapters/native-bridge.ts index 92e029d..15aa6cb 100644 --- a/packages/core/src/adapters/native-bridge.ts +++ b/packages/core/src/adapters/native-bridge.ts @@ -70,27 +70,38 @@ export class NativeBridgeAdapter implements ControllerAdapter { } capabilities() { + const includeState = this.options.includeState !== false; + const includeExtensions = this.options.includeExtensions !== false; + const includeProfileHidReport = + this.options.includeProfileHidReport !== false; return createAdapterCapabilities({ supportsStateSync: true, supportsXInputReports: true, supportsNativeBridge: true, - supportsTouchpad: true, - supportsGyro: true, - supportsDeviceStatus: true, + supportsTouchpad: + includeState || includeExtensions || includeProfileHidReport, + supportsGyro: + includeState || includeExtensions || includeProfileHidReport, + supportsDeviceStatus: includeState || includeExtensions, supportedCommands: controllerCommandTypes, outputFormats: [ - "controller-state", + ...(includeState ? (["controller-state"] as const) : []), "xinput-report", "hid-gamepad-report", - "hid-playstation-extended-report", - "hid-switch-extended-report", + ...(includeProfileHidReport + ? ([ + "hid-playstation-extended-report", + "hid-switch-extended-report", + ] as const) + : []), "native-bridge-jsonl", ], reportFormats: [ "xinput", "hid-gamepad", - "hid-playstation-extended", - "hid-switch-extended", + ...(includeProfileHidReport + ? (["hid-playstation-extended", "hid-switch-extended"] as const) + : []), ], transport: "callback", }); diff --git a/packages/core/src/adapters/native-process.ts b/packages/core/src/adapters/native-process.ts index 98d2ebd..d0cc534 100644 --- a/packages/core/src/adapters/native-process.ts +++ b/packages/core/src/adapters/native-process.ts @@ -163,6 +163,10 @@ export class NativeProcessBridgeAdapter implements ControllerAdapter { } capabilities() { + const includeState = this.options.includeState !== false; + const includeExtensions = this.options.includeExtensions !== false; + const includeProfileHidReport = + this.options.includeProfileHidReport !== false; const supportsRumble = this.options.supportsRumble ?? false; const supportsLights = this.options.supportsLights ?? false; const supportsVirtualDevice = this.options.supportsVirtualDevice ?? true; @@ -176,27 +180,34 @@ export class NativeProcessBridgeAdapter implements ControllerAdapter { supportsNativeBridge: true, supportsRumble, supportsLights, - supportsTouchpad: true, - supportsGyro: true, - supportsDeviceStatus: true, + supportsTouchpad: + includeState || includeExtensions || includeProfileHidReport, + supportsGyro: + includeState || includeExtensions || includeProfileHidReport, + supportsDeviceStatus: includeState || includeExtensions, supportsVirtualDevice, supportedCommands: controllerCommandTypes, requiresNativeInstall: this.options.requiresNativeInstall ?? true, requiresElevatedPermissions: this.options.requiresElevatedPermissions ?? false, outputFormats: [ - "controller-state", + ...(includeState ? (["controller-state"] as const) : []), "xinput-report", "hid-gamepad-report", - "hid-playstation-extended-report", - "hid-switch-extended-report", + ...(includeProfileHidReport + ? ([ + "hid-playstation-extended-report", + "hid-switch-extended-report", + ] as const) + : []), "native-bridge-jsonl", ], reportFormats: [ "xinput", "hid-gamepad", - "hid-playstation-extended", - "hid-switch-extended", + ...(includeProfileHidReport + ? (["hid-playstation-extended", "hid-switch-extended"] as const) + : []), ...feedbackReportFormats, ], feedbackTypes: [