diff --git a/.changeset/chilly-parents-build.md b/.changeset/chilly-parents-build.md new file mode 100644 index 00000000..73dfad9d --- /dev/null +++ b/.changeset/chilly-parents-build.md @@ -0,0 +1,6 @@ +--- +"@yume-chan/adb-scrcpy": patch +"@yume-chan/scrcpy": patch +--- + +Fix UHID output stream doesn't work from server version 2.6 diff --git a/libraries/adb-scrcpy/src/client.ts b/libraries/adb-scrcpy/src/client.ts index f538d12c..c5694d3f 100644 --- a/libraries/adb-scrcpy/src/client.ts +++ b/libraries/adb-scrcpy/src/client.ts @@ -25,6 +25,7 @@ import { PushReadableStream, SplitStringStream, TextDecoderStream, + tryCancel, WritableStream, } from "@yume-chan/stream-extra"; import { ExactReadableEndedError } from "@yume-chan/struct"; @@ -321,22 +322,24 @@ export class AdbScrcpyClient> { const buffered = new BufferedReadableStream(controlStream); try { while (true) { - let type: number; + let id: number; try { const result = await buffered.readExactly(1); - type = result[0]!; + id = result[0]!; } catch (e) { if (e instanceof ExactReadableEndedError) { - this.#options.endDeviceMessageStream(); + this.#options.deviceMessageParsers.close(); break; } + throw e; } - await this.#options.parseDeviceMessage(type, buffered); + + await this.#options.deviceMessageParsers.parse(id, buffered); } } catch (e) { - this.#options.endDeviceMessageStream(e); - buffered.cancel(e).catch(() => {}); + this.#options.deviceMessageParsers.error(e); + await tryCancel(buffered); } } diff --git a/libraries/scrcpy/src/1_15/impl/clipboard-stream.ts b/libraries/scrcpy/src/1_15/impl/clipboard-stream.ts index 841604f1..7a0e43d5 100644 --- a/libraries/scrcpy/src/1_15/impl/clipboard-stream.ts +++ b/libraries/scrcpy/src/1_15/impl/clipboard-stream.ts @@ -16,6 +16,8 @@ export class ClipboardStream { #controller: PushReadableStreamController; + readonly id = 0; + constructor() { let controller!: PushReadableStreamController; super((controller_) => { @@ -24,13 +26,9 @@ export class ClipboardStream this.#controller = controller; } - async parse(id: number, stream: AsyncExactReadable): Promise { - if (id === 0) { - const message = await ClipboardDeviceMessage.deserialize(stream); - await this.#controller.enqueue(message.content); - return true; - } - return false; + async parse(_id: number, stream: AsyncExactReadable): Promise { + const message = await ClipboardDeviceMessage.deserialize(stream); + await this.#controller.enqueue(message.content); } close() { diff --git a/libraries/scrcpy/src/1_15/options.ts b/libraries/scrcpy/src/1_15/options.ts index 742f9b8a..0cc348e4 100644 --- a/libraries/scrcpy/src/1_15/options.ts +++ b/libraries/scrcpy/src/1_15/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -10,6 +9,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -47,11 +47,18 @@ export class ScrcpyOptions1_15 implements ScrcpyOptions { return this.#clipboard; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control) { - this.#clipboard = new ClipboardStream(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); } } @@ -73,25 +80,6 @@ export class ScrcpyOptions1_15 implements ScrcpyOptions { return parseVideoStreamMetadata(stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard!.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard!.error(e); - } else { - this.#clipboard!.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_17/options.ts b/libraries/scrcpy/src/1_17/options.ts index a1f7c965..9cab6e48 100644 --- a/libraries/scrcpy/src/1_17/options.ts +++ b/libraries/scrcpy/src/1_17/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -54,11 +54,18 @@ export class ScrcpyOptions1_17 return this.#clipboard; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control) { - this.#clipboard = new ClipboardStream(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); } } @@ -88,25 +95,6 @@ export class ScrcpyOptions1_17 return parseVideoStreamMetadata(stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard!.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard!.error(e); - } else { - this.#clipboard!.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_18/options.ts b/libraries/scrcpy/src/1_18/options.ts index b287b9bb..96c57273 100644 --- a/libraries/scrcpy/src/1_18/options.ts +++ b/libraries/scrcpy/src/1_18/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -54,11 +54,18 @@ export class ScrcpyOptions1_18 return this.#clipboard; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control) { - this.#clipboard = new ClipboardStream(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); } } @@ -88,25 +95,6 @@ export class ScrcpyOptions1_18 return parseVideoStreamMetadata(stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard!.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard!.error(e); - } else { - this.#clipboard!.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_21/impl/set-clipboard.ts b/libraries/scrcpy/src/1_21/impl/set-clipboard.ts index dee3f315..b740e76a 100644 --- a/libraries/scrcpy/src/1_21/impl/set-clipboard.ts +++ b/libraries/scrcpy/src/1_21/impl/set-clipboard.ts @@ -29,18 +29,15 @@ export class AckClipboardHandler implements ScrcpyDeviceMessageParser { #closed = false; - async parse(id: number, stream: AsyncExactReadable) { - if (id !== 1) { - return false; - } + readonly id = 1; + async parse(_id: number, stream: AsyncExactReadable): Promise { const message = await AckClipboardDeviceMessage.deserialize(stream); const resolver = this.#resolvers.get(message.sequence); if (resolver) { resolver.resolve(); this.#resolvers.delete(message.sequence); } - return true; } close(): void { diff --git a/libraries/scrcpy/src/1_21/options.ts b/libraries/scrcpy/src/1_21/options.ts index 3dbc0e2e..8b9a42a5 100644 --- a/libraries/scrcpy/src/1_21/options.ts +++ b/libraries/scrcpy/src/1_21/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -55,12 +55,22 @@ export class ScrcpyOptions1_21 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -90,31 +100,6 @@ export class ScrcpyOptions1_21 return parseVideoStreamMetadata(stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_22/options.ts b/libraries/scrcpy/src/1_22/options.ts index 9061c793..88938b47 100644 --- a/libraries/scrcpy/src/1_22/options.ts +++ b/libraries/scrcpy/src/1_22/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -55,12 +55,22 @@ export class ScrcpyOptions1_22 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -90,31 +100,6 @@ export class ScrcpyOptions1_22 return parseVideoStreamMetadata(this.value, stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_23/options.ts b/libraries/scrcpy/src/1_23/options.ts index c009fd85..026a364e 100644 --- a/libraries/scrcpy/src/1_23/options.ts +++ b/libraries/scrcpy/src/1_23/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -55,12 +55,22 @@ export class ScrcpyOptions1_23 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -90,31 +100,6 @@ export class ScrcpyOptions1_23 return parseVideoStreamMetadata(this.value, stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_24/options.ts b/libraries/scrcpy/src/1_24/options.ts index 843815d1..83fa7218 100644 --- a/libraries/scrcpy/src/1_24/options.ts +++ b/libraries/scrcpy/src/1_24/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -55,12 +55,22 @@ export class ScrcpyOptions1_24 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -90,31 +100,6 @@ export class ScrcpyOptions1_24 return parseVideoStreamMetadata(this.value, stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/1_25/options.ts b/libraries/scrcpy/src/1_25/options.ts index 98f3953c..cec5b1b7 100644 --- a/libraries/scrcpy/src/1_25/options.ts +++ b/libraries/scrcpy/src/1_25/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyControlMessageType, @@ -12,6 +11,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -55,12 +55,22 @@ export class ScrcpyOptions1_25 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -90,31 +100,6 @@ export class ScrcpyOptions1_25 return parseVideoStreamMetadata(this.value, stream); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_0/options.ts b/libraries/scrcpy/src/2_0/options.ts index 4bb99841..654fedf9 100644 --- a/libraries/scrcpy/src/2_0/options.ts +++ b/libraries/scrcpy/src/2_0/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -56,12 +56,22 @@ export class ScrcpyOptions2_0 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init }; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -97,31 +107,6 @@ export class ScrcpyOptions2_0 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_1/options.ts b/libraries/scrcpy/src/2_1/options.ts index 439ab495..55223707 100644 --- a/libraries/scrcpy/src/2_1/options.ts +++ b/libraries/scrcpy/src/2_1/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -56,12 +56,22 @@ export class ScrcpyOptions2_1 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -97,31 +107,6 @@ export class ScrcpyOptions2_1 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_2/options.ts b/libraries/scrcpy/src/2_2/options.ts index ede6b349..bee1f75c 100644 --- a/libraries/scrcpy/src/2_2/options.ts +++ b/libraries/scrcpy/src/2_2/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -56,6 +56,11 @@ export class ScrcpyOptions2_2 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -64,8 +69,13 @@ export class ScrcpyOptions2_2 } if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -101,31 +111,6 @@ export class ScrcpyOptions2_2 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_3/options.ts b/libraries/scrcpy/src/2_3/options.ts index f5d4bab1..d523f0b3 100644 --- a/libraries/scrcpy/src/2_3/options.ts +++ b/libraries/scrcpy/src/2_3/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -56,6 +56,11 @@ export class ScrcpyOptions2_3 #ackClipboardHandler: AckClipboardHandler | undefined; + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -64,8 +69,13 @@ export class ScrcpyOptions2_3 } if (this.value.control && this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } } @@ -101,31 +111,6 @@ export class ScrcpyOptions2_3 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_4/impl/uhid-output-stream.ts b/libraries/scrcpy/src/2_4/impl/uhid-output-stream.ts index a37b9d8a..f0544786 100644 --- a/libraries/scrcpy/src/2_4/impl/uhid-output-stream.ts +++ b/libraries/scrcpy/src/2_4/impl/uhid-output-stream.ts @@ -23,6 +23,8 @@ export class UHidOutputStream { #controller: PushReadableStreamController; + readonly id = 2; + constructor() { let controller!: PushReadableStreamController; super((controller_) => { @@ -31,14 +33,9 @@ export class UHidOutputStream this.#controller = controller; } - async parse(id: number, stream: AsyncExactReadable): Promise { - if (id !== 2) { - return false; - } - + async parse(_id: number, stream: AsyncExactReadable): Promise { const message = await UHidOutputDeviceMessage.deserialize(stream); await this.#controller.enqueue(message); - return true; } close() { diff --git a/libraries/scrcpy/src/2_4/options.ts b/libraries/scrcpy/src/2_4/options.ts index 9e21f9f3..d9bb7eec 100644 --- a/libraries/scrcpy/src/2_4/options.ts +++ b/libraries/scrcpy/src/2_4/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -67,6 +67,11 @@ export class ScrcpyOptions2_4 return this.#uHidOutput; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -76,11 +81,18 @@ export class ScrcpyOptions2_4 if (this.value.control) { if (this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } - this.#uHidOutput = new UHidOutputStream(); + this.#uHidOutput = this.#deviceMessageParsers.add( + new UHidOutputStream(), + ); } } @@ -116,37 +128,6 @@ export class ScrcpyOptions2_4 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - if (await this.#uHidOutput?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - this.#uHidOutput?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - this.#uHidOutput?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_6/options.ts b/libraries/scrcpy/src/2_6/options.ts index 979c7ac1..778a901e 100644 --- a/libraries/scrcpy/src/2_6/options.ts +++ b/libraries/scrcpy/src/2_6/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -67,6 +67,11 @@ export class ScrcpyOptions2_6 return this.#uHidOutput; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -80,11 +85,18 @@ export class ScrcpyOptions2_6 if (this.value.control) { if (this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } - this.#uHidOutput = new UHidOutputStream(); + this.#uHidOutput = this.#deviceMessageParsers.add( + new UHidOutputStream(), + ); } } @@ -120,31 +132,6 @@ export class ScrcpyOptions2_6 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/2_7/impl/serialize-uhid-create.ts b/libraries/scrcpy/src/2_7/impl/serialize-uhid-create.ts index e27be5c0..b3be3ae0 100644 --- a/libraries/scrcpy/src/2_7/impl/serialize-uhid-create.ts +++ b/libraries/scrcpy/src/2_7/impl/serialize-uhid-create.ts @@ -1,19 +1,17 @@ import type { StructInit } from "@yume-chan/struct"; import { buffer, string, struct, u16, u8 } from "@yume-chan/struct"; -import { ScrcpyControlMessageType } from "../../base/control-message-type.js"; import type { ScrcpyUHidCreateControlMessage } from "../../latest.js"; -export const UHidCreateControlMessage = /* #__PURE__ */ (() => - struct( - { - type: u8(ScrcpyControlMessageType.UHidCreate), - id: u16, - name: string(u8), - data: buffer(u16), - }, - { littleEndian: false }, - ))(); +export const UHidCreateControlMessage = struct( + { + type: u8, + id: u16, + name: string(u8), + data: buffer(u16), + }, + { littleEndian: false }, +); export type UHidCreateControlMessage = StructInit< typeof UHidCreateControlMessage diff --git a/libraries/scrcpy/src/2_7/options.ts b/libraries/scrcpy/src/2_7/options.ts index 95629010..498858b6 100644 --- a/libraries/scrcpy/src/2_7/options.ts +++ b/libraries/scrcpy/src/2_7/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -67,6 +67,11 @@ export class ScrcpyOptions2_7 return this.#uHidOutput; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -80,11 +85,18 @@ export class ScrcpyOptions2_7 if (this.value.control) { if (this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } - this.#uHidOutput = new UHidOutputStream(); + this.#uHidOutput = this.#deviceMessageParsers.add( + new UHidOutputStream(), + ); } } @@ -120,31 +132,6 @@ export class ScrcpyOptions2_7 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/3_0/options.ts b/libraries/scrcpy/src/3_0/options.ts index 6f253afa..00be08b5 100644 --- a/libraries/scrcpy/src/3_0/options.ts +++ b/libraries/scrcpy/src/3_0/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -67,6 +67,11 @@ export class ScrcpyOptions3_0 return this.#uHidOutput; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -80,11 +85,18 @@ export class ScrcpyOptions3_0 if (this.value.control) { if (this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } - this.#uHidOutput = new UHidOutputStream(); + this.#uHidOutput = this.#deviceMessageParsers.add( + new UHidOutputStream(), + ); } } @@ -120,31 +132,6 @@ export class ScrcpyOptions3_0 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/3_1/impl/serialize-uhid-create.ts b/libraries/scrcpy/src/3_1/impl/serialize-uhid-create.ts index 8799c706..85013408 100644 --- a/libraries/scrcpy/src/3_1/impl/serialize-uhid-create.ts +++ b/libraries/scrcpy/src/3_1/impl/serialize-uhid-create.ts @@ -1,21 +1,19 @@ import type { StructInit } from "@yume-chan/struct"; import { buffer, string, struct, u16, u8 } from "@yume-chan/struct"; -import { ScrcpyControlMessageType } from "../../base/control-message-type.js"; import type { ScrcpyUHidCreateControlMessage } from "../../latest.js"; -export const UHidCreateControlMessage = /* #__PURE__ */ (() => - struct( - { - type: u8(ScrcpyControlMessageType.UHidCreate), - id: u16, - vendorId: u16, - productId: u16, - name: string(u8), - data: buffer(u16), - }, - { littleEndian: false }, - ))(); +export const UHidCreateControlMessage = struct( + { + type: u8, + id: u16, + vendorId: u16, + productId: u16, + name: string(u8), + data: buffer(u16), + }, + { littleEndian: false }, +); export type UHidCreateControlMessage = StructInit< typeof UHidCreateControlMessage diff --git a/libraries/scrcpy/src/3_1/options.ts b/libraries/scrcpy/src/3_1/options.ts index e5c293b8..cfe97c80 100644 --- a/libraries/scrcpy/src/3_1/options.ts +++ b/libraries/scrcpy/src/3_1/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -67,6 +67,11 @@ export class ScrcpyOptions3_1 return this.#uHidOutput; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -80,11 +85,18 @@ export class ScrcpyOptions3_1 if (this.value.control) { if (this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } - this.#uHidOutput = new UHidOutputStream(); + this.#uHidOutput = this.#deviceMessageParsers.add( + new UHidOutputStream(), + ); } } @@ -120,31 +132,6 @@ export class ScrcpyOptions3_1 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/3_2/options.ts b/libraries/scrcpy/src/3_2/options.ts index 89409b34..1b15dd30 100644 --- a/libraries/scrcpy/src/3_2/options.ts +++ b/libraries/scrcpy/src/3_2/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyAudioStreamMetadata, @@ -13,6 +12,7 @@ import type { ScrcpyScrollController, ScrcpyVideoStream, } from "../base/index.js"; +import { ScrcpyDeviceMessageParsers } from "../base/index.js"; import type { ScrcpyBackOrScreenOnControlMessage, ScrcpyInjectTouchControlMessage, @@ -67,6 +67,11 @@ export class ScrcpyOptions3_2 return this.#uHidOutput; } + #deviceMessageParsers = new ScrcpyDeviceMessageParsers(); + get deviceMessageParsers() { + return this.#deviceMessageParsers; + } + constructor(init: Init) { this.value = { ...Defaults, ...init } as never; @@ -80,11 +85,18 @@ export class ScrcpyOptions3_2 if (this.value.control) { if (this.value.clipboardAutosync) { - this.#clipboard = new ClipboardStream(); - this.#ackClipboardHandler = new AckClipboardHandler(); + this.#clipboard = this.#deviceMessageParsers.add( + new ClipboardStream(), + ); + + this.#ackClipboardHandler = this.#deviceMessageParsers.add( + new AckClipboardHandler(), + ); } - this.#uHidOutput = new UHidOutputStream(); + this.#uHidOutput = this.#deviceMessageParsers.add( + new UHidOutputStream(), + ); } } @@ -120,31 +132,6 @@ export class ScrcpyOptions3_2 return parseAudioStreamMetadata(stream, this.value); } - async parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise { - if (await this.#clipboard?.parse(id, stream)) { - return; - } - - if (await this.#ackClipboardHandler?.parse(id, stream)) { - return; - } - - throw new Error("Unknown device message"); - } - - endDeviceMessageStream(e?: unknown): void { - if (e) { - this.#clipboard?.error(e); - this.#ackClipboardHandler?.error(e); - } else { - this.#clipboard?.close(); - this.#ackClipboardHandler?.close(); - } - } - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/base/device-message.ts b/libraries/scrcpy/src/base/device-message.ts index 3c0c205b..3c880630 100644 --- a/libraries/scrcpy/src/base/device-message.ts +++ b/libraries/scrcpy/src/base/device-message.ts @@ -1,9 +1,58 @@ import type { AsyncExactReadable } from "@yume-chan/struct"; export interface ScrcpyDeviceMessageParser { - parse(id: number, stream: AsyncExactReadable): Promise; + readonly id: number | readonly number[]; + + parse(id: number, stream: AsyncExactReadable): Promise; close(): void; error(e?: unknown): void; } + +export class ScrcpyDeviceMessageParsers { + #parsers: ScrcpyDeviceMessageParser[] = []; + get parsers(): readonly ScrcpyDeviceMessageParser[] { + return this.#parsers; + } + + #add(id: number, parser: ScrcpyDeviceMessageParser) { + if (this.#parsers[id]) { + throw new Error(`Duplicate parser for id ${id}`); + } + this.#parsers[id] = parser; + } + + add(parser: T): T { + if (Array.isArray(parser.id)) { + for (const id of parser.id) { + this.#add(id as number, parser); + } + } else { + this.#add(parser.id as number, parser); + } + + return parser; + } + + async parse(id: number, stream: AsyncExactReadable): Promise { + const parser = this.#parsers[id]; + if (!parser) { + throw new Error(`Unknown device message id ${id}`); + } + + return parser.parse(id, stream); + } + + close() { + for (const parser of this.#parsers) { + parser.close(); + } + } + + error(e?: unknown) { + for (const parser of this.#parsers) { + parser.error(e); + } + } +} diff --git a/libraries/scrcpy/src/base/options.ts b/libraries/scrcpy/src/base/options.ts index e0ef088a..c25cdadb 100644 --- a/libraries/scrcpy/src/base/options.ts +++ b/libraries/scrcpy/src/base/options.ts @@ -1,6 +1,5 @@ import type { MaybePromiseLike } from "@yume-chan/async"; import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra"; -import type { AsyncExactReadable, ExactReadable } from "@yume-chan/struct"; import type { ScrcpyBackOrScreenOnControlMessage, @@ -12,6 +11,7 @@ import type { import type { ScrcpyAudioStreamMetadata } from "./audio.js"; import type { ScrcpyControlMessageType } from "./control-message-type.js"; +import type { ScrcpyDeviceMessageParsers } from "./device-message.js"; import type { ScrcpyDisplay } from "./display.js"; import type { ScrcpyEncoder } from "./encoder.js"; import type { ScrcpyMediaStreamPacket } from "./media.js"; @@ -29,6 +29,8 @@ export interface ScrcpyOptions { | ReadableStream | undefined; + readonly deviceMessageParsers: ScrcpyDeviceMessageParsers; + serialize(): string[]; setListDisplays(): void; @@ -43,13 +45,6 @@ export interface ScrcpyOptions { stream: ReadableStream, ): MaybePromiseLike; - parseDeviceMessage( - id: number, - stream: ExactReadable | AsyncExactReadable, - ): Promise; - - endDeviceMessageStream(e?: unknown): void; - createMediaStreamTransformer(): TransformStream< Uint8Array, ScrcpyMediaStreamPacket diff --git a/libraries/scrcpy/src/control/message-type-map.ts b/libraries/scrcpy/src/control/message-type-map.ts index d374b63b..ce5a5f2e 100644 --- a/libraries/scrcpy/src/control/message-type-map.ts +++ b/libraries/scrcpy/src/control/message-type-map.ts @@ -20,11 +20,11 @@ export class ScrcpyControlMessageTypeMap { return value; } - fillMessageType( + fillMessageType( message: Omit, - type: T["type"], + type: ScrcpyControlMessageType, ): T { - (message as T).type = this.get(type) as ScrcpyControlMessageType; + (message as T).type = this.get(type); return message as T; } }