diff --git a/libraries/adb-scrcpy/src/3_2.ts b/libraries/adb-scrcpy/src/3_2.ts new file mode 100644 index 00000000..e41b412d --- /dev/null +++ b/libraries/adb-scrcpy/src/3_2.ts @@ -0,0 +1,50 @@ +import type { Adb, AdbNoneProtocolSpawner } from "@yume-chan/adb"; +import type { ScrcpyDisplay, ScrcpyEncoder } from "@yume-chan/scrcpy"; +import { ScrcpyOptions3_2 } from "@yume-chan/scrcpy"; + +import { + createConnection, + getDisplays, + getEncoders, +} from "./2_1/impl/index.js"; +import type { AdbScrcpyClientOptions } from "./client-options.js"; +import type { AdbScrcpyConnection } from "./connection.js"; +import type { AdbScrcpyOptions, AdbScrcpyOptionsGetEncoders } from "./types.js"; + +export class AdbScrcpyOptions3_2 + extends ScrcpyOptions3_2 + implements + AdbScrcpyOptions>, + AdbScrcpyOptionsGetEncoders +{ + readonly version: string; + + readonly spawner: AdbNoneProtocolSpawner | undefined; + + constructor( + init: ScrcpyOptions3_2.Init, + clientOptions?: AdbScrcpyClientOptions, + ) { + super(init); + + this.version = clientOptions?.version ?? "3.2"; + this.spawner = clientOptions?.spawner; + } + + getEncoders(adb: Adb, path: string): Promise { + return getEncoders(adb, path, this); + } + + getDisplays(adb: Adb, path: string): Promise { + return getDisplays(adb, path, this); + } + + createConnection(adb: Adb): AdbScrcpyConnection { + return createConnection(adb, this.value); + } +} + +export namespace AdbScrcpyOptions3_2 { + export type Init = + ScrcpyOptions3_2.Init; +} diff --git a/libraries/adb-scrcpy/src/index.ts b/libraries/adb-scrcpy/src/index.ts index d17c7aa7..10875180 100644 --- a/libraries/adb-scrcpy/src/index.ts +++ b/libraries/adb-scrcpy/src/index.ts @@ -22,6 +22,7 @@ export * from "./3_0.js"; export * from "./3_0_1.js"; export * from "./3_0_2.js"; export * from "./3_1.js"; +export * from "./3_2.js"; export * from "./client-options.js"; export * from "./client.js"; export * from "./connection.js"; diff --git a/libraries/adb-scrcpy/src/latest.ts b/libraries/adb-scrcpy/src/latest.ts index dc8fb383..25ff9e85 100644 --- a/libraries/adb-scrcpy/src/latest.ts +++ b/libraries/adb-scrcpy/src/latest.ts @@ -1,11 +1,11 @@ -import { AdbScrcpyOptions3_1 } from "./3_1.js"; +import { AdbScrcpyOptions3_2 } from "./3_2.js"; import type { AdbScrcpyClientOptions } from "./client-options.js"; export class AdbScrcpyOptionsLatest< TVideo extends boolean, -> extends AdbScrcpyOptions3_1 { +> extends AdbScrcpyOptions3_2 { constructor( - init: AdbScrcpyOptions3_1.Init, + init: AdbScrcpyOptions3_2.Init, clientOptions?: AdbScrcpyClientOptions, ) { super(init, clientOptions); @@ -14,5 +14,5 @@ export class AdbScrcpyOptionsLatest< export namespace AdbScrcpyOptionsLatest { export type Init = - AdbScrcpyOptions3_1.Init; + AdbScrcpyOptions3_2.Init; } diff --git a/libraries/scrcpy/src/1_15/impl/defaults.ts b/libraries/scrcpy/src/1_15/impl/defaults.ts index 26f60c04..3070ddaa 100644 --- a/libraries/scrcpy/src/1_15/impl/defaults.ts +++ b/libraries/scrcpy/src/1_15/impl/defaults.ts @@ -1,19 +1,18 @@ import type { Init } from "./init.js"; import { VideoOrientation } from "./init.js"; -export const Defaults = /* #__PURE__ */ (() => - ({ - logLevel: "debug", - maxSize: 0, - bitRate: 8_000_000, - maxFps: 0, - lockVideoOrientation: VideoOrientation.Unlocked, - tunnelForward: false, - crop: undefined, - sendFrameMeta: true, - control: true, - displayId: 0, - showTouches: false, - stayAwake: false, - codecOptions: undefined, - }) as const satisfies Required)(); +export const Defaults = { + logLevel: "debug", + maxSize: 0, + bitRate: 8_000_000, + maxFps: 0, + lockVideoOrientation: VideoOrientation.Unlocked, + tunnelForward: false, + crop: undefined, + sendFrameMeta: true, + control: true, + displayId: 0, + showTouches: false, + stayAwake: false, + codecOptions: undefined, +} as const satisfies Required; diff --git a/libraries/scrcpy/src/3_2/impl/defaults.ts b/libraries/scrcpy/src/3_2/impl/defaults.ts new file mode 100644 index 00000000..73906a29 --- /dev/null +++ b/libraries/scrcpy/src/3_2/impl/defaults.ts @@ -0,0 +1,8 @@ +import type { Init } from "./init.js"; +import { PrevImpl } from "./prev.js"; + +export const Defaults = /* #__PURE__ */ (() => + ({ + ...PrevImpl.Defaults, + displayImePolicy: undefined, + }) as const satisfies Required>)(); diff --git a/libraries/scrcpy/src/3_2/impl/index.ts b/libraries/scrcpy/src/3_2/impl/index.ts new file mode 100644 index 00000000..0e8c571c --- /dev/null +++ b/libraries/scrcpy/src/3_2/impl/index.ts @@ -0,0 +1,3 @@ +export * from "../../3_1/impl/index.js"; +export { Defaults } from "./defaults.js"; +export type { Init } from "./init.js"; diff --git a/libraries/scrcpy/src/3_2/impl/init.ts b/libraries/scrcpy/src/3_2/impl/init.ts new file mode 100644 index 00000000..075bf178 --- /dev/null +++ b/libraries/scrcpy/src/3_2/impl/init.ts @@ -0,0 +1,17 @@ +import type { PrevImpl } from "./prev.js"; + +export interface Init + extends Omit, "audioSource"> { + audioSource?: + | PrevImpl.Init["audioSource"] + | "mic-unprocessed" + | "mic-camcorder" + | "mic-voice-recognition" + | "mic-voice-communication" + | "voice-call" + | "voice-call-uplink" + | "voice-call-downlink" + | "voice-performance"; + + displayImePolicy?: "local" | "fallback" | "hide" | undefined; +} diff --git a/libraries/scrcpy/src/3_2/impl/prev.ts b/libraries/scrcpy/src/3_2/impl/prev.ts new file mode 100644 index 00000000..14554cea --- /dev/null +++ b/libraries/scrcpy/src/3_2/impl/prev.ts @@ -0,0 +1 @@ +export * as PrevImpl from "../../3_1/impl/index.js"; diff --git a/libraries/scrcpy/src/3_2/index.ts b/libraries/scrcpy/src/3_2/index.ts new file mode 100644 index 00000000..e9939706 --- /dev/null +++ b/libraries/scrcpy/src/3_2/index.ts @@ -0,0 +1 @@ +export * from "./options.js"; diff --git a/libraries/scrcpy/src/3_2/options.ts b/libraries/scrcpy/src/3_2/options.ts new file mode 100644 index 00000000..89409b34 --- /dev/null +++ b/libraries/scrcpy/src/3_2/options.ts @@ -0,0 +1,190 @@ +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, + ScrcpyControlMessageType, + ScrcpyDisplay, + ScrcpyEncoder, + ScrcpyMediaStreamPacket, + ScrcpyOptions, + ScrcpyOptionsListEncoders, + ScrcpyScrollController, + ScrcpyVideoStream, +} from "../base/index.js"; +import type { + ScrcpyBackOrScreenOnControlMessage, + ScrcpyInjectTouchControlMessage, + ScrcpySetClipboardControlMessage, + ScrcpyUHidCreateControlMessage, + ScrcpyUHidOutputDeviceMessage, +} from "../latest.js"; + +import type { Init } from "./impl/index.js"; +import { + AckClipboardHandler, + ClipboardStream, + ControlMessageTypes, + createMediaStreamTransformer, + createScrollController, + Defaults, + parseAudioStreamMetadata, + parseDisplay, + parseEncoder, + parseVideoStreamMetadata, + serialize, + serializeBackOrScreenOnControlMessage, + serializeInjectTouchControlMessage, + serializeUHidCreateControlMessage, + setListDisplays, + setListEncoders, + UHidOutputStream, +} from "./impl/index.js"; + +export class ScrcpyOptions3_2 + implements ScrcpyOptions>, ScrcpyOptionsListEncoders +{ + static readonly Defaults = Defaults; + + readonly value: Required>; + + get controlMessageTypes(): readonly ScrcpyControlMessageType[] { + return ControlMessageTypes; + } + + #clipboard: ClipboardStream | undefined; + get clipboard(): ReadableStream | undefined { + return this.#clipboard; + } + + #ackClipboardHandler: AckClipboardHandler | undefined; + + #uHidOutput: UHidOutputStream | undefined; + get uHidOutput(): + | ReadableStream + | undefined { + return this.#uHidOutput; + } + + constructor(init: Init) { + this.value = { ...Defaults, ...init } as never; + + if (this.value.videoSource === "camera") { + this.value.control = false; + } + + if (this.value.audioDup) { + this.value.audioSource = "playback"; + } + + if (this.value.control) { + if (this.value.clipboardAutosync) { + this.#clipboard = new ClipboardStream(); + this.#ackClipboardHandler = new AckClipboardHandler(); + } + + this.#uHidOutput = new UHidOutputStream(); + } + } + + serialize(): string[] { + return serialize>(this.value, Defaults); + } + + setListDisplays(): void { + setListDisplays(this.value); + } + + parseDisplay(line: string): ScrcpyDisplay | undefined { + return parseDisplay(line); + } + + setListEncoders() { + setListEncoders(this.value); + } + + parseEncoder(line: string): ScrcpyEncoder | undefined { + return parseEncoder(line); + } + + parseVideoStreamMetadata( + stream: ReadableStream, + ): MaybePromiseLike { + return parseVideoStreamMetadata(this.value, stream); + } + + parseAudioStreamMetadata( + stream: ReadableStream, + ): MaybePromiseLike { + 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 + > { + return createMediaStreamTransformer(this.value); + } + + serializeInjectTouchControlMessage( + message: ScrcpyInjectTouchControlMessage, + ): Uint8Array { + return serializeInjectTouchControlMessage(message); + } + + serializeBackOrScreenOnControlMessage( + message: ScrcpyBackOrScreenOnControlMessage, + ): Uint8Array | undefined { + return serializeBackOrScreenOnControlMessage(message); + } + + serializeSetClipboardControlMessage( + message: ScrcpySetClipboardControlMessage, + ): Uint8Array | [Uint8Array, Promise] { + return this.#ackClipboardHandler!.serializeSetClipboardControlMessage( + message, + ); + } + + createScrollController(): ScrcpyScrollController { + return createScrollController(); + } + + serializeUHidCreateControlMessage( + message: ScrcpyUHidCreateControlMessage, + ): Uint8Array { + return serializeUHidCreateControlMessage(message); + } +} + +type Init_ = Init; + +export namespace ScrcpyOptions3_2 { + export type Init = Init_; +} diff --git a/libraries/scrcpy/src/index.ts b/libraries/scrcpy/src/index.ts index fcb8769a..9d5deecb 100644 --- a/libraries/scrcpy/src/index.ts +++ b/libraries/scrcpy/src/index.ts @@ -25,6 +25,7 @@ export * from "./3_0/index.js"; export * from "./3_0_1.js"; export * from "./3_0_2.js"; export * from "./3_1/index.js"; +export * from "./3_2/index.js"; export * from "./android/index.js"; export * from "./base/index.js"; export * from "./codec/index.js"; diff --git a/libraries/scrcpy/src/latest.ts b/libraries/scrcpy/src/latest.ts index 53865dd8..020fe688 100644 --- a/libraries/scrcpy/src/latest.ts +++ b/libraries/scrcpy/src/latest.ts @@ -1,16 +1,16 @@ -import { ScrcpyOptions3_1 } from "./3_1/options.js"; +import { ScrcpyOptions3_2 } from "./3_2/options.js"; export class ScrcpyOptionsLatest< TVideo extends boolean, -> extends ScrcpyOptions3_1 { - constructor(init: ScrcpyOptions3_1.Init) { +> extends ScrcpyOptions3_2 { + constructor(init: ScrcpyOptions3_2.Init) { super(init); } } export namespace ScrcpyOptionsLatest { export type Init = - ScrcpyOptions3_1.Init; + ScrcpyOptions3_2.Init; } export { @@ -28,4 +28,4 @@ export { SetClipboardControlMessage as ScrcpySetClipboardControlMessage, UHidCreateControlMessage as ScrcpyUHidCreateControlMessage, UHidOutputDeviceMessage as ScrcpyUHidOutputDeviceMessage, -} from "./3_1/impl/index.js"; +} from "./3_2/impl/index.js";