refactor(scrcpy) make large enums tree-shakeable (#798)

This commit is contained in:
Simon Chan 2025-09-11 10:31:01 +08:00 committed by GitHub
parent b2512856be
commit 27a6614680
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
89 changed files with 1056 additions and 1089 deletions

View file

@ -0,0 +1,7 @@
---
"@yume-chan/scrcpy": major
---
Convert `ScrcpyOptionsX_YY.prototype.controlMessageTypes` to a mapping from internal control message types to version-specific values.
Generally, `ScrcpyControlMessageSerializer`, `ScrcpyControlMessageWriter`, or `AdbScrcpyClient.prototype.controller` should be used to send control messages to devices, instead of using `controlMessageTypes` directly.

View file

@ -143,16 +143,6 @@ export class AdbDaemonWebUsbConnection
new MaybeConsumable.WritableStream({
write: async (chunk) => {
try {
if (
typeof SharedArrayBuffer !== "undefined" &&
chunk.buffer instanceof SharedArrayBuffer
) {
// Copy data to a non-shared ArrayBuffer
const copy = new Uint8Array(chunk.byteLength);
copy.set(chunk);
chunk = copy;
}
await device.raw.transferOut(
outEndpoint.endpointNumber,
// WebUSB doesn't support SharedArrayBuffer

View file

@ -27,7 +27,7 @@ export class AdbScrcpyOptions3_3_2<TVideo extends boolean>
) {
super(init);
this.version = clientOptions?.version ?? "3.3.1";
this.version = clientOptions?.version ?? "3.3.2";
this.spawner = clientOptions?.spawner;
}

View file

@ -3,7 +3,5 @@ export function toLocalUint8Array(value: Uint8Array): Uint8Array<ArrayBuffer> {
return value as Uint8Array<ArrayBuffer>;
}
const copy = new Uint8Array(value.length);
copy.set(value);
return copy;
return new Uint8Array(value);
}

View file

@ -862,21 +862,21 @@ export function parseShortTermReferencePictureSet(
export const AspectRatioIndicator = {
Unspecified: 0,
Square: 1,
_12_11: 2,
_10_11: 3,
_16_11: 4,
_40_33: 5,
_24_11: 6,
_20_11: 7,
_32_11: 8,
_80_33: 9,
_18_11: 10,
_15_11: 11,
_64_33: 12,
_160_99: 13,
_4_3: 15,
_3_2: 16,
_2_1: 17,
["12:11"]: 2,
["10:11"]: 3,
["16:11"]: 4,
["40:33"]: 5,
["24:11"]: 6,
["20:11"]: 7,
["32:11"]: 8,
["80:33"]: 9,
["18:11"]: 10,
["15:11"]: 11,
["64:33"]: 12,
["160:99"]: 13,
["4:3"]: 15,
["3:2"]: 16,
["2:1"]: 17,
Extended: 255,
} as const;

View file

@ -1,16 +1,16 @@
import type { ScrcpyControlMessageTypeMap } from "../../base/index.js";
import { ScrcpyControlMessageType } from "../../base/index.js";
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] =
/* #__PURE__ */ (() => [
/* 0 */ ScrcpyControlMessageType.InjectKeyCode,
/* 1 */ ScrcpyControlMessageType.InjectText,
/* 2 */ ScrcpyControlMessageType.InjectTouch,
/* 3 */ ScrcpyControlMessageType.InjectScroll,
/* 4 */ ScrcpyControlMessageType.BackOrScreenOn,
/* 5 */ ScrcpyControlMessageType.ExpandNotificationPanel,
/* 6 */ ScrcpyControlMessageType.CollapseNotificationPanel,
/* 7 */ ScrcpyControlMessageType.GetClipboard,
/* 8 */ ScrcpyControlMessageType.SetClipboard,
/* 9 */ ScrcpyControlMessageType.SetDisplayPower,
/* 10 */ ScrcpyControlMessageType.RotateDevice,
])();
export const ControlMessageTypes = {
[ScrcpyControlMessageType.InjectKeyCode]: 0,
[ScrcpyControlMessageType.InjectText]: 1,
[ScrcpyControlMessageType.InjectTouch]: 2,
[ScrcpyControlMessageType.InjectScroll]: 3,
[ScrcpyControlMessageType.BackOrScreenOn]: 4,
[ScrcpyControlMessageType.ExpandNotificationPanel]: 5,
[ScrcpyControlMessageType.CollapseNotificationPanel]: 6,
[ScrcpyControlMessageType.GetClipboard]: 7,
[ScrcpyControlMessageType.SetClipboard]: 8,
[ScrcpyControlMessageType.SetDisplayPower]: 9,
[ScrcpyControlMessageType.RotateDevice]: 10,
} as const satisfies ScrcpyControlMessageTypeMap;

View file

@ -32,6 +32,7 @@ export const PointerId = {
export const InjectTouchControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
action: u8<AndroidMotionEventAction>(),
pointerId: u64,

View file

@ -6,6 +6,7 @@ import type { ScrcpyInjectScrollControlMessage } from "../../latest.js";
export const InjectScrollControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
pointerX: u32,
pointerY: u32,

View file

@ -4,7 +4,11 @@ import { string, struct, u32, u8 } from "@yume-chan/struct";
import type { ScrcpySetClipboardControlMessage } from "../../latest.js";
export const SetClipboardControlMessage = struct(
{ type: u8, content: string(u32) },
{
// value of `type` can change between versions
type: u8,
content: string(u32),
},
{ littleEndian: false },
);

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyMediaStreamPacket,
ScrcpyOptions,
@ -38,7 +37,7 @@ export class ScrcpyOptions1_15 implements ScrcpyOptions<Init> {
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}

View file

@ -1,11 +1 @@
import { ScrcpyOptions1_15 } from "./1_15/index.js";
export class ScrcpyOptions1_15_1 extends ScrcpyOptions1_15 {
constructor(init: ScrcpyOptions1_15.Init) {
super(init);
}
}
export namespace ScrcpyOptions1_15_1 {
export type Init = ScrcpyOptions1_15.Init;
}
export { ScrcpyOptions1_15 as ScrcpyOptions1_15_1 } from "./1_15/index.js";

View file

@ -1,11 +1 @@
import { ScrcpyOptions1_15 } from "./1_15/index.js";
export class ScrcpyOptions1_16 extends ScrcpyOptions1_15 {
constructor(init: ScrcpyOptions1_15.Init) {
super(init);
}
}
export namespace ScrcpyOptions1_16 {
export type Init = ScrcpyOptions1_15.Init;
}
export { ScrcpyOptions1_15 as ScrcpyOptions1_16 } from "./1_15/index.js";

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
encoderName: undefined,
}) as const satisfies Required<Init>)();
export const Defaults = {
...PrevImpl.Defaults,
encoderName: undefined,
} as const satisfies Required<Init>;

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -45,7 +44,7 @@ export class ScrcpyOptions1_17
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}

View file

@ -1,10 +1,17 @@
import type { ScrcpyControlMessageTypeMap } from "../../base/index.js";
import { ScrcpyControlMessageType } from "../../base/index.js";
import { PrevImpl } from "./prev.js";
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] =
/* #__PURE__ */ (() => {
const result = PrevImpl.ControlMessageTypes.slice();
result.splice(6, 0, ScrcpyControlMessageType.ExpandSettingPanel);
return result;
})();
export const ControlMessageTypes = {
[ScrcpyControlMessageType.InjectKeyCode]: 0,
[ScrcpyControlMessageType.InjectText]: 1,
[ScrcpyControlMessageType.InjectTouch]: 2,
[ScrcpyControlMessageType.InjectScroll]: 3,
[ScrcpyControlMessageType.BackOrScreenOn]: 4,
[ScrcpyControlMessageType.ExpandNotificationPanel]: 5,
[ScrcpyControlMessageType.ExpandSettingPanel]: 6,
[ScrcpyControlMessageType.CollapseNotificationPanel]: 7,
[ScrcpyControlMessageType.GetClipboard]: 8,
[ScrcpyControlMessageType.SetClipboard]: 9,
[ScrcpyControlMessageType.SetDisplayPower]: 10,
[ScrcpyControlMessageType.RotateDevice]: 11,
} as const satisfies ScrcpyControlMessageTypeMap;

View file

@ -2,10 +2,9 @@ import type { Init } from "./init.js";
import { VideoOrientation } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
logLevel: "debug",
lockVideoOrientation: VideoOrientation.Unlocked,
powerOffOnClose: false,
}) as const satisfies Required<Init>)();
export const Defaults = {
...PrevImpl.Defaults,
logLevel: "debug",
lockVideoOrientation: VideoOrientation.Unlocked,
powerOffOnClose: false,
} as const satisfies Required<Init>;

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -45,7 +44,7 @@ export class ScrcpyOptions1_18
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}

View file

@ -1,11 +1 @@
import { ScrcpyOptions1_18 } from "./1_18/index.js";
export class ScrcpyOptions1_19 extends ScrcpyOptions1_18 {
constructor(init: ScrcpyOptions1_18.Init) {
super(init);
}
}
export namespace ScrcpyOptions1_19 {
export type Init = ScrcpyOptions1_18.Init;
}
export { ScrcpyOptions1_18 as ScrcpyOptions1_19 } from "./1_18/index.js";

View file

@ -1,11 +1 @@
import { ScrcpyOptions1_18 } from "./1_18/index.js";
export class ScrcpyOptions1_20 extends ScrcpyOptions1_18 {
constructor(init: ScrcpyOptions1_18.Init) {
super(init);
}
}
export namespace ScrcpyOptions1_20 {
export type Init = ScrcpyOptions1_18.Init;
}
export { ScrcpyOptions1_18 as ScrcpyOptions1_20 } from "./1_18/index.js";

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
clipboardAutosync: true,
}) as const satisfies Required<Init>)();
export const Defaults = {
...PrevImpl.Defaults,
clipboardAutosync: true,
} as const satisfies Required<Init>;

View file

@ -7,4 +7,5 @@ export {
AckClipboardDeviceMessage,
AckClipboardHandler,
SetClipboardControlMessage,
serializeSetClipboardControlMessage,
} from "./set-clipboard.js";

View file

@ -12,6 +12,7 @@ export const AckClipboardDeviceMessage = struct(
export const SetClipboardControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
sequence: u64,
paste: u8<boolean>(),
@ -75,3 +76,16 @@ export class AckClipboardHandler implements ScrcpyDeviceMessageParser {
];
}
}
export function serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
ackHandler: AckClipboardHandler | undefined,
): Uint8Array | [Uint8Array, Promise<void>] {
if (!ackHandler) {
throw new Error(
"`serializeSetClipboardControlMessage` requires `control: true` option",
);
}
return ackHandler.serializeSetClipboardControlMessage(message);
}

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -33,6 +32,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -44,7 +44,7 @@ export class ScrcpyOptions1_21
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -63,10 +63,12 @@ export class ScrcpyOptions1_21
constructor(init: Init) {
this.value = { ...Defaults, ...init };
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -122,8 +124,9 @@ export class ScrcpyOptions1_21
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,10 +1,9 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
downsizeOnError: true,
sendDeviceMeta: true,
sendDummyByte: true,
}) as const satisfies Required<Init>)();
export const Defaults = {
...PrevImpl.Defaults,
downsizeOnError: true,
sendDeviceMeta: true,
sendDummyByte: true,
} as const satisfies Required<Init>;

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -33,6 +32,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -44,7 +44,7 @@ export class ScrcpyOptions1_22
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -63,10 +63,12 @@ export class ScrcpyOptions1_22
constructor(init: Init) {
this.value = { ...Defaults, ...init };
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -122,8 +124,9 @@ export class ScrcpyOptions1_22
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
cleanup: true,
}) as const satisfies Required<Init>)();
export const Defaults = {
...PrevImpl.Defaults,
cleanup: true,
} as const satisfies Required<Init>;

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -33,6 +32,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -44,7 +44,7 @@ export class ScrcpyOptions1_23
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -63,10 +63,12 @@ export class ScrcpyOptions1_23
constructor(init: Init) {
this.value = { ...Defaults, ...init };
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -122,8 +124,9 @@ export class ScrcpyOptions1_23
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
powerOn: true,
}) as const satisfies Required<Init>)();
export const Defaults = {
...PrevImpl.Defaults,
powerOn: true,
} as const satisfies Required<Init>;

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -33,6 +32,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -44,7 +44,7 @@ export class ScrcpyOptions1_24
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -63,10 +63,12 @@ export class ScrcpyOptions1_24
constructor(init: Init) {
this.value = { ...Defaults, ...init };
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -122,8 +124,9 @@ export class ScrcpyOptions1_24
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -3,7 +3,6 @@ import type { Field, StructInit } from "@yume-chan/struct";
import { field, struct, u16, u32, u8 } from "@yume-chan/struct";
import type { ScrcpyScrollController } from "../../base/index.js";
import { ScrcpyControlMessageType } from "../../base/index.js";
import type { ScrcpyInjectScrollControlMessage } from "../../latest.js";
import { clamp } from "../../utils/index.js";
@ -24,20 +23,20 @@ export const SignedFloat: Field<number, never, never> = field(
},
);
export const InjectScrollControlMessage = /* #__PURE__ */ (() =>
struct(
{
type: u8(ScrcpyControlMessageType.InjectScroll),
pointerX: u32,
pointerY: u32,
videoWidth: u16,
videoHeight: u16,
scrollX: SignedFloat,
scrollY: SignedFloat,
buttons: u32,
},
{ littleEndian: false },
))();
export const InjectScrollControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
pointerX: u32,
pointerY: u32,
videoWidth: u16,
videoHeight: u16,
scrollX: SignedFloat,
scrollY: SignedFloat,
buttons: u32,
},
{ littleEndian: false },
);
export type InjectScrollControlMessage = StructInit<
typeof InjectScrollControlMessage

View file

@ -2,7 +2,6 @@ import type { MaybePromiseLike } from "@yume-chan/async";
import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -33,6 +32,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -44,7 +44,7 @@ export class ScrcpyOptions1_25
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -63,10 +63,12 @@ export class ScrcpyOptions1_25
constructor(init: Init) {
this.value = { ...Defaults, ...init };
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -122,8 +124,9 @@ export class ScrcpyOptions1_25
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,25 +1,30 @@
import { omit } from "../../utils/index.js";
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...omit(PrevImpl.Defaults, "bitRate", "codecOptions", "encoderName"),
scid: undefined,
export const Defaults = {
...{
...PrevImpl.Defaults,
// Remove obsolete values
// relies on the minifier to flatten the nested spread
bitRate: undefined,
codecOptions: undefined,
encoderName: undefined,
},
videoCodec: "h264",
videoBitRate: 8000000,
videoCodecOptions: undefined,
videoEncoder: undefined,
scid: undefined,
audio: true,
audioCodec: "opus",
audioBitRate: 128000,
audioCodecOptions: undefined,
audioEncoder: undefined,
videoCodec: "h264",
videoBitRate: 8000000,
videoCodecOptions: undefined,
videoEncoder: undefined,
listEncoders: false,
listDisplays: false,
sendCodecMeta: true,
}) as const satisfies Required<Init>)();
audio: true,
audioCodec: "opus",
audioBitRate: 128000,
audioCodecOptions: undefined,
audioEncoder: undefined,
listEncoders: false,
listDisplays: false,
sendCodecMeta: true,
} as const satisfies Required<Init>;

View file

@ -2,27 +2,26 @@ import type { StructInit } from "@yume-chan/struct";
import { struct, u16, u32, u64, u8 } from "@yume-chan/struct";
import type { AndroidMotionEventAction } from "../../android/motion-event.js";
import { ScrcpyControlMessageType } from "../../base/control-message-type.js";
import type { ScrcpyInjectTouchControlMessage } from "../../latest.js";
import { PrevImpl } from "./prev.js";
export const InjectTouchControlMessage = /* #__PURE__ */ (() =>
struct(
{
type: u8(ScrcpyControlMessageType.InjectTouch),
action: u8<AndroidMotionEventAction>(),
pointerId: u64,
pointerX: u32,
pointerY: u32,
videoWidth: u16,
videoHeight: u16,
pressure: PrevImpl.UnsignedFloat,
actionButton: u32,
buttons: u32,
},
{ littleEndian: false },
))();
export const InjectTouchControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
action: u8<AndroidMotionEventAction>(),
pointerId: u64,
pointerX: u32,
pointerY: u32,
videoWidth: u16,
videoHeight: u16,
pressure: PrevImpl.UnsignedFloat,
actionButton: u32,
buttons: u32,
},
{ littleEndian: false },
);
export type InjectTouchControlMessage = StructInit<
typeof InjectTouchControlMessage

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -34,6 +33,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -45,7 +45,7 @@ export class ScrcpyOptions2_0
readonly value: Required<Init>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -64,10 +64,12 @@ export class ScrcpyOptions2_0
constructor(init: Init) {
this.value = { ...Defaults, ...init };
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -129,8 +131,9 @@ export class ScrcpyOptions2_0
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,9 +1,8 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
video: true,
audioSource: "output",
}) as const satisfies Required<Init<true>>)();
export const Defaults = {
...PrevImpl.Defaults,
video: true,
audioSource: "output",
} as const satisfies Required<Init<true>>;

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -34,6 +33,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -45,7 +45,7 @@ export class ScrcpyOptions2_1<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -64,10 +64,12 @@ export class ScrcpyOptions2_1<TVideo extends boolean>
constructor(init: Init<TVideo>) {
this.value = { ...Defaults, ...init } as never;
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -129,8 +131,9 @@ export class ScrcpyOptions2_1<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions2_1 } from "./2_1/index.js";
export class ScrcpyOptions2_1_1<
TVideo extends boolean,
> extends ScrcpyOptions2_1<TVideo> {
constructor(init: ScrcpyOptions2_1.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions2_1_1 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions2_1.Init<TVideo>;
}
export { ScrcpyOptions2_1 as ScrcpyOptions2_1_1 } from "./2_1/index.js";

View file

@ -1,17 +1,16 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
videoSource: "display",
displayId: 0,
cameraId: undefined,
cameraSize: undefined,
cameraFacing: undefined,
cameraAr: undefined,
cameraFps: undefined,
cameraHighSpeed: false,
listCameras: false,
listCameraSizes: false,
}) as const satisfies Required<Init<true>>)();
export const Defaults = {
...PrevImpl.Defaults,
videoSource: "display",
displayId: 0,
cameraId: undefined,
cameraSize: undefined,
cameraFacing: undefined,
cameraAr: undefined,
cameraFps: undefined,
cameraHighSpeed: false,
listCameras: false,
listCameraSizes: false,
} as const satisfies Required<Init<true>>;

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -34,6 +33,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -45,7 +45,7 @@ export class ScrcpyOptions2_2<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -68,10 +68,12 @@ export class ScrcpyOptions2_2<TVideo extends boolean>
this.value.control = false;
}
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -133,8 +135,9 @@ export class ScrcpyOptions2_2<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -34,6 +33,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
setListDisplays,
setListEncoders,
} from "./impl/index.js";
@ -45,7 +45,7 @@ export class ScrcpyOptions2_3<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -68,10 +68,12 @@ export class ScrcpyOptions2_3<TVideo extends boolean>
this.value.control = false;
}
if (this.value.control && this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
if (this.value.control) {
if (this.value.clipboardAutosync) {
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
@ -133,8 +135,9 @@ export class ScrcpyOptions2_3<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions2_3 } from "./2_3/index.js";
export class ScrcpyOptions2_3_1<
TVideo extends boolean,
> extends ScrcpyOptions2_3<TVideo> {
constructor(init: ScrcpyOptions2_3.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions2_3_1 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions2_3.Init<TVideo>;
}
export { ScrcpyOptions2_3 as ScrcpyOptions2_3_1 } from "./2_3/index.js";

View file

@ -1,11 +1,20 @@
import type { ScrcpyControlMessageTypeMap } from "../../base/index.js";
import { ScrcpyControlMessageType } from "../../base/index.js";
import { PrevImpl } from "./prev.js";
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] =
/* #__PURE__ */ (() => [
...PrevImpl.ControlMessageTypes,
ScrcpyControlMessageType.UHidCreate,
ScrcpyControlMessageType.UHidInput,
ScrcpyControlMessageType.OpenHardKeyboardSettings,
])();
export const ControlMessageTypes = {
[ScrcpyControlMessageType.InjectKeyCode]: 0,
[ScrcpyControlMessageType.InjectText]: 1,
[ScrcpyControlMessageType.InjectTouch]: 2,
[ScrcpyControlMessageType.InjectScroll]: 3,
[ScrcpyControlMessageType.BackOrScreenOn]: 4,
[ScrcpyControlMessageType.ExpandNotificationPanel]: 5,
[ScrcpyControlMessageType.ExpandSettingPanel]: 6,
[ScrcpyControlMessageType.CollapseNotificationPanel]: 7,
[ScrcpyControlMessageType.GetClipboard]: 8,
[ScrcpyControlMessageType.SetClipboard]: 9,
[ScrcpyControlMessageType.SetDisplayPower]: 10,
[ScrcpyControlMessageType.RotateDevice]: 11,
[ScrcpyControlMessageType.UHidCreate]: 12,
[ScrcpyControlMessageType.UHidInput]: 13,
[ScrcpyControlMessageType.OpenHardKeyboardSettings]: 14,
} as const satisfies ScrcpyControlMessageTypeMap;

View file

@ -5,6 +5,7 @@ import type { ScrcpyUHidCreateControlMessage } from "../../latest.js";
export const UHidCreateControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
id: u16,
data: buffer(u16),

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions2_4<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -84,12 +84,12 @@ export class ScrcpyOptions2_4<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -150,8 +150,9 @@ export class ScrcpyOptions2_4<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions2_4 } from "./2_4/index.js";
export class ScrcpyOptions2_5<
TVideo extends boolean,
> extends ScrcpyOptions2_4<TVideo> {
constructor(init: ScrcpyOptions2_4.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions2_5 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions2_4.Init<TVideo>;
}
export { ScrcpyOptions2_4 as ScrcpyOptions2_5 } from "./2_4/index.js";

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
audioDup: false,
}) as const satisfies Required<Init<true>>)();
export const Defaults = {
...PrevImpl.Defaults,
audioDup: false,
} as const satisfies Required<Init<true>>;

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions2_6<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -88,12 +88,12 @@ export class ScrcpyOptions2_6<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -154,8 +154,9 @@ export class ScrcpyOptions2_6<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions2_6 } from "./2_6/index.js";
export class ScrcpyOptions2_6_1<
TVideo extends boolean,
> extends ScrcpyOptions2_6<TVideo> {
constructor(init: ScrcpyOptions2_6.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions2_6_1 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions2_6.Init<TVideo>;
}
export { ScrcpyOptions2_6 as ScrcpyOptions2_6_1 } from "./2_6/index.js";

View file

@ -1,10 +1,21 @@
import type { ScrcpyControlMessageTypeMap } from "../../base/index.js";
import { ScrcpyControlMessageType } from "../../base/index.js";
import { PrevImpl } from "./prev.js";
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] =
/* #__PURE__ */ (() => {
const result = PrevImpl.ControlMessageTypes.slice();
result.splice(14, 0, ScrcpyControlMessageType.UHidDestroy);
return result;
})();
export const ControlMessageTypes = {
[ScrcpyControlMessageType.InjectKeyCode]: 0,
[ScrcpyControlMessageType.InjectText]: 1,
[ScrcpyControlMessageType.InjectTouch]: 2,
[ScrcpyControlMessageType.InjectScroll]: 3,
[ScrcpyControlMessageType.BackOrScreenOn]: 4,
[ScrcpyControlMessageType.ExpandNotificationPanel]: 5,
[ScrcpyControlMessageType.ExpandSettingPanel]: 6,
[ScrcpyControlMessageType.CollapseNotificationPanel]: 7,
[ScrcpyControlMessageType.GetClipboard]: 8,
[ScrcpyControlMessageType.SetClipboard]: 9,
[ScrcpyControlMessageType.SetDisplayPower]: 10,
[ScrcpyControlMessageType.RotateDevice]: 11,
[ScrcpyControlMessageType.UHidCreate]: 12,
[ScrcpyControlMessageType.UHidInput]: 13,
[ScrcpyControlMessageType.UHidDestroy]: 14,
[ScrcpyControlMessageType.OpenHardKeyboardSettings]: 15,
} as const satisfies ScrcpyControlMessageTypeMap;

View file

@ -5,6 +5,7 @@ import type { ScrcpyUHidCreateControlMessage } from "../../latest.js";
export const UHidCreateControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
id: u16,
name: string(u8),

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions2_7<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -88,12 +88,12 @@ export class ScrcpyOptions2_7<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -154,8 +154,9 @@ export class ScrcpyOptions2_7<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,10 +1,23 @@
import type { ScrcpyControlMessageTypeMap } from "../../base/index.js";
import { ScrcpyControlMessageType } from "../../base/index.js";
import { PrevImpl } from "./prev.js";
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] =
/* #__PURE__ */ (() => [
...PrevImpl.ControlMessageTypes,
ScrcpyControlMessageType.StartApp,
ScrcpyControlMessageType.ResetVideo,
])();
export const ControlMessageTypes = {
[ScrcpyControlMessageType.InjectKeyCode]: 0,
[ScrcpyControlMessageType.InjectText]: 1,
[ScrcpyControlMessageType.InjectTouch]: 2,
[ScrcpyControlMessageType.InjectScroll]: 3,
[ScrcpyControlMessageType.BackOrScreenOn]: 4,
[ScrcpyControlMessageType.ExpandNotificationPanel]: 5,
[ScrcpyControlMessageType.ExpandSettingPanel]: 6,
[ScrcpyControlMessageType.CollapseNotificationPanel]: 7,
[ScrcpyControlMessageType.GetClipboard]: 8,
[ScrcpyControlMessageType.SetClipboard]: 9,
[ScrcpyControlMessageType.SetDisplayPower]: 10,
[ScrcpyControlMessageType.RotateDevice]: 11,
[ScrcpyControlMessageType.UHidCreate]: 12,
[ScrcpyControlMessageType.UHidInput]: 13,
[ScrcpyControlMessageType.UHidDestroy]: 14,
[ScrcpyControlMessageType.OpenHardKeyboardSettings]: 15,
[ScrcpyControlMessageType.StartApp]: 16,
[ScrcpyControlMessageType.ResetVideo]: 17,
} as const satisfies ScrcpyControlMessageTypeMap;

View file

@ -1,15 +1,18 @@
import { omit } from "../../utils/index.js";
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...omit(PrevImpl.Defaults, "lockVideoOrientation"),
captureOrientation: undefined,
angle: 0,
screenOffTimeout: undefined,
listApps: false,
newDisplay: undefined,
vdSystemDecorations: true,
}) as const satisfies Required<Init<true>>)();
export const Defaults = {
...{
...PrevImpl.Defaults,
// Remove obsolete values
// relies on the minifier to flatten the nested spread
lockVideoOrientation: undefined,
},
captureOrientation: undefined,
angle: 0,
screenOffTimeout: undefined,
listApps: false,
newDisplay: undefined,
vdSystemDecorations: true,
} as const satisfies Required<Init<true>>;

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions3_0<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -88,12 +88,12 @@ export class ScrcpyOptions3_0<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -154,8 +154,9 @@ export class ScrcpyOptions3_0<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions3_0 } from "./3_0/index.js";
export class ScrcpyOptions3_0_1<
TVideo extends boolean,
> extends ScrcpyOptions3_0<TVideo> {
constructor(init: ScrcpyOptions3_0.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions3_0_1 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions3_0.Init<TVideo>;
}
export { ScrcpyOptions3_0 as ScrcpyOptions3_0_1 } from "./3_0/index.js";

View file

@ -1,14 +1 @@
import { ScrcpyOptions3_0 } from "./3_0/index.js";
export class ScrcpyOptions3_0_2<
TVideo extends boolean,
> extends ScrcpyOptions3_0<TVideo> {
constructor(init: ScrcpyOptions3_0.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions3_0_2 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions3_0.Init<TVideo>;
}
export { ScrcpyOptions3_0 as ScrcpyOptions3_0_2 } from "./3_0/index.js";

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
vdDestroyContent: false,
}) as const satisfies Required<Init<true>>)();
export const Defaults = {
...PrevImpl.Defaults,
vdDestroyContent: false,
} as const satisfies Required<Init<true>>;

View file

@ -5,6 +5,7 @@ import type { ScrcpyUHidCreateControlMessage } from "../../latest.js";
export const UHidCreateControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
id: u16,
vendorId: u16,

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions3_1<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -88,12 +88,12 @@ export class ScrcpyOptions3_1<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -154,8 +154,9 @@ export class ScrcpyOptions3_1<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,8 +1,7 @@
import type { Init } from "./init.js";
import { PrevImpl } from "./prev.js";
export const Defaults = /* #__PURE__ */ (() =>
({
...PrevImpl.Defaults,
displayImePolicy: undefined,
}) as const satisfies Required<Init<true>>)();
export const Defaults = {
...PrevImpl.Defaults,
displayImePolicy: undefined,
} as const satisfies Required<Init<true>>;

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions3_2<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -88,12 +88,12 @@ export class ScrcpyOptions3_2<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -154,8 +154,9 @@ export class ScrcpyOptions3_2<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions3_2 } from "./3_2/index.js";
export class ScrcpyOptions3_3<
TVideo extends boolean,
> extends ScrcpyOptions3_2<TVideo> {
constructor(init: ScrcpyOptions3_2.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions3_3 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions3_2.Init<TVideo>;
}
export { ScrcpyOptions3_2 as ScrcpyOptions3_3 } from "./3_2/index.js";

View file

@ -3,7 +3,6 @@ import type { ReadableStream, TransformStream } from "@yume-chan/stream-extra";
import type {
ScrcpyAudioStreamMetadata,
ScrcpyControlMessageType,
ScrcpyDisplay,
ScrcpyEncoder,
ScrcpyMediaStreamPacket,
@ -36,6 +35,7 @@ import {
serialize,
serializeBackOrScreenOnControlMessage,
serializeInjectTouchControlMessage,
serializeSetClipboardControlMessage,
serializeUHidCreateControlMessage,
setListDisplays,
setListEncoders,
@ -49,7 +49,7 @@ export class ScrcpyOptions3_3_1<TVideo extends boolean>
readonly value: Required<Init<TVideo>>;
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
get controlMessageTypes(): typeof ControlMessageTypes {
return ControlMessageTypes;
}
@ -88,12 +88,12 @@ export class ScrcpyOptions3_3_1<TVideo extends boolean>
this.#clipboard = this.#deviceMessageParsers.add(
new ClipboardStream(),
);
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
}
this.#ackClipboardHandler = this.#deviceMessageParsers.add(
new AckClipboardHandler(),
);
this.#uHidOutput = this.#deviceMessageParsers.add(
new UHidOutputStream(),
);
@ -154,8 +154,9 @@ export class ScrcpyOptions3_3_1<TVideo extends boolean>
serializeSetClipboardControlMessage(
message: ScrcpySetClipboardControlMessage,
): Uint8Array | [Uint8Array, Promise<void>] {
return this.#ackClipboardHandler!.serializeSetClipboardControlMessage(
return serializeSetClipboardControlMessage(
message,
this.#ackClipboardHandler,
);
}

View file

@ -1,14 +1 @@
import { ScrcpyOptions3_3_1 } from "./3_3_1/index.js";
export class ScrcpyOptions3_3_2<
TVideo extends boolean,
> extends ScrcpyOptions3_3_1<TVideo> {
constructor(init: ScrcpyOptions3_3_1.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptions3_3_2 {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions3_3_1.Init<TVideo>;
}
export { ScrcpyOptions3_3_1 as ScrcpyOptions3_3_2 } from "./3_3_1/index.js";

View file

@ -0,0 +1,194 @@
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/KeyEvent.java;l=97;drc=95c1165bb895dd844e1793460710f7163dd330a3
// Android key code to Chrome key code: https://source.chromium.org/chromium/chromium/src/+/main:ui/events/keycodes/keyboard_code_conversion_android.cc
// Chrome key code to DOM key code: https://source.chromium.org/chromium/chromium/src/+/main:ui/events/keycodes/dom/dom_code_data.inc
// Some keys are not mapped to `KeyboardEvent.code`, only to `KeyboardEvent.key`: https://source.chromium.org/chromium/chromium/src/+/main:ui/events/keycodes/dom/dom_key_data.inc
export const AndroidHome = 3;
export const AndroidBack = 4;
export const AndroidCall = 5;
export const AndroidEndCall = 6;
export const Digit0 = 7;
export const Digit1 = 8;
export const Digit2 = 9;
export const Digit3 = 10;
export const Digit4 = 11;
export const Digit5 = 12;
export const Digit6 = 13;
export const Digit7 = 14;
export const Digit8 = 15;
export const Digit9 = 16;
/**
* '*' key.
*/
export const Star = 17; // Name not verified
/**
* '#' key.
*/
export const Pound = 18; // Name not verified
/**
* Directional Pad Up key.
*/
export const ArrowUp = 19;
/**
* Directional Pad Down key.
*/
export const ArrowDown = 20;
/**
* Directional Pad Left key.
*/
export const ArrowLeft = 21;
/**
* Directional Pad Right key.
*/
export const ArrowRight = 22;
/**
* Directional Pad Center key.
*/
export const AndroidDPadCenter = 23;
export const VolumeUp = 24; // Name not verified
export const VolumeDown = 25; // Name not verified
export const Power = 26; // Name not verified
export const AndroidCamera = 27;
export const Clear = 28; // Name not verified
export const KeyA = 29;
export const KeyB = 30;
export const KeyC = 31;
export const KeyD = 32;
export const KeyE = 33;
export const KeyF = 34;
export const KeyG = 35;
export const KeyH = 36;
export const KeyI = 37;
export const KeyJ = 38;
export const KeyK = 39;
export const KeyL = 40;
export const KeyM = 41;
export const KeyN = 42;
export const KeyO = 43;
export const KeyP = 44;
export const KeyQ = 45;
export const KeyR = 46;
export const KeyS = 47;
export const KeyT = 48;
export const KeyU = 49;
export const KeyV = 50;
export const KeyW = 51;
export const KeyX = 52;
export const KeyY = 53;
export const KeyZ = 54;
export const Comma = 55;
export const Period = 56;
export const AltLeft = 57;
export const AltRight = 58;
export const ShiftLeft = 59;
export const ShiftRight = 60;
export const Tab = 61;
export const Space = 62;
export const AndroidSymbol = 63;
export const AndroidExplorer = 64;
export const AndroidEnvelope = 65;
export const Enter = 66;
export const Backspace = 67;
export const Backquote = 68;
export const Minus = 69;
export const Equal = 70;
export const BracketLeft = 71;
export const BracketRight = 72;
export const Backslash = 73;
export const Semicolon = 74;
export const Quote = 75;
export const Slash = 76;
export const At = 77; // Name not verified
/**
* Number modifier key.
*
* Used to enter numeric symbols.
* This key is not Num Lock; it is more like {@link AltLeft} and is
* interpreted as an ALT key by `android.text.method.MetaKeyKeyListener`.
*/
export const AndroidNum = 78;
/**
* Headset Hook key.
*
* Used to hang up calls and stop media.
*/
export const AndroidHeadsetHook = 79;
/**
* Camera Focus key.
*
* Used to focus the camera.
*/
export const AndroidFocus = 80;
export const Plus = 81; // Name not verified
export const ContextMenu = 82;
export const AndroidNotification = 83;
export const AndroidSearch = 84;
export const PageUp = 92;
export const PageDown = 93;
export const Escape = 111;
export const Delete = 112;
export const ControlLeft = 113;
export const ControlRight = 114;
export const CapsLock = 115;
export const ScrollLock = 116;
export const MetaLeft = 117;
export const MetaRight = 118;
export const AndroidFunction = 119;
export const PrintScreen = 120;
export const Pause = 121;
export const Home = 122;
export const End = 123;
export const Insert = 124;
export const AndroidForward = 125;
export const F1 = 131;
export const F2 = 132;
export const F3 = 133;
export const F4 = 134;
export const F5 = 135;
export const F6 = 136;
export const F7 = 137;
export const F8 = 138;
export const F9 = 139;
export const F10 = 140;
export const F11 = 141;
export const F12 = 142;
export const NumLock = 143;
export const Numpad0 = 144;
export const Numpad1 = 145;
export const Numpad2 = 146;
export const Numpad3 = 147;
export const Numpad4 = 148;
export const Numpad5 = 149;
export const Numpad6 = 150;
export const Numpad7 = 151;
export const Numpad8 = 152;
export const Numpad9 = 153;
export const NumpadDivide = 154;
export const NumpadMultiply = 155;
export const NumpadSubtract = 156;
export const NumpadAdd = 157;
export const NumpadDecimal = 158;
export const NumpadComma = 159; // Name not verified
export const NumpadEnter = 160;
export const NumpadEquals = 161; // Name not verified
export const NumpadLeftParen = 162; // Name not verified
export const NumpadRightParen = 163; // Name not verified
export const VolumeMute = 164; // Name not verified
export const AndroidAppSwitch = 187; // Name not verified
export const AndroidCut = 277;
export const AndroidCopy = 278;
export const AndroidPaste = 279;

View file

@ -1,3 +1,5 @@
import * as AndroidKeyCode from "./key-code-value.js";
export const AndroidKeyEventAction = {
Down: 0,
Up: 1,
@ -29,189 +31,10 @@ export const AndroidKeyEventMeta = {
export type AndroidKeyEventMeta =
(typeof AndroidKeyEventMeta)[keyof typeof AndroidKeyEventMeta];
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/KeyEvent.java;l=97;drc=95c1165bb895dd844e1793460710f7163dd330a3
// Android key code to Chrome key code: https://source.chromium.org/chromium/chromium/src/+/main:ui/events/keycodes/keyboard_code_conversion_android.cc
// Chrome key code to DOM key code: https://source.chromium.org/chromium/chromium/src/+/main:ui/events/keycodes/dom/dom_code_data.inc
// Some keys are not mapped to `KeyboardEvent.code`, only to `KeyboardEvent.key`: https://source.chromium.org/chromium/chromium/src/+/main:ui/events/keycodes/dom/dom_key_data.inc
export const AndroidKeyCode = {
AndroidHome: 3,
AndroidBack: 4,
AndroidCall: 5,
AndroidEndCall: 6,
// biome-ignore lint/suspicious/noRedeclare: TypeScript declaration merging for enum-like object
type AndroidKeyCode = (typeof AndroidKeyCode)[keyof typeof AndroidKeyCode];
Digit0: 7,
Digit1: 8,
Digit2: 9,
Digit3: 10,
Digit4: 11,
Digit5: 12,
Digit6: 13,
Digit7: 14,
Digit8: 15,
Digit9: 16,
/**
* '*' key.
*/
Star: 17, // Name not verified
/**
* '#' key.
*/
Pound: 18, // Name not verified
/**
* Directional Pad Up key.
*/
ArrowUp: 19,
/**
* Directional Pad Down key.
*/
ArrowDown: 20,
/**
* Directional Pad Left key.
*/
ArrowLeft: 21,
/**
* Directional Pad Right key.
*/
ArrowRight: 22,
/**
* Directional Pad Center key.
*/
AndroidDPadCenter: 23,
VolumeUp: 24, // Name not verified
VolumeDown: 25, // Name not verified
Power: 26, // Name not verified
AndroidCamera: 27,
Clear: 28, // Name not verified
KeyA: 29,
KeyB: 30,
KeyC: 31,
KeyD: 32,
KeyE: 33,
KeyF: 34,
KeyG: 35,
KeyH: 36,
KeyI: 37,
KeyJ: 38,
KeyK: 39,
KeyL: 40,
KeyM: 41,
KeyN: 42,
KeyO: 43,
KeyP: 44,
KeyQ: 45,
KeyR: 46,
KeyS: 47,
KeyT: 48,
KeyU: 49,
KeyV: 50,
KeyW: 51,
KeyX: 52,
KeyY: 53,
KeyZ: 54,
Comma: 55,
Period: 56,
AltLeft: 57,
AltRight: 58,
ShiftLeft: 59,
ShiftRight: 60,
Tab: 61,
Space: 62,
AndroidSymbol: 63,
AndroidExplorer: 64,
AndroidEnvelope: 65,
Enter: 66,
Backspace: 67,
Backquote: 68,
Minus: 69,
Equal: 70,
BracketLeft: 71,
BracketRight: 72,
Backslash: 73,
Semicolon: 74,
Quote: 75,
Slash: 76,
At: 77, // Name not verified
AndroidNum: 78,
AndroidHeadsetHook: 79,
/**
* Camera Focus key
*/
AndroidFocus: 80,
Plus: 81, // Name not verified
ContextMenu: 82,
AndroidNotification: 83,
AndroidSearch: 84,
PageUp: 92,
PageDown: 93,
Escape: 111,
Delete: 112,
ControlLeft: 113,
ControlRight: 114,
CapsLock: 115,
ScrollLock: 116,
MetaLeft: 117,
MetaRight: 118,
AndroidFunction: 119,
PrintScreen: 120,
Pause: 121,
Home: 122,
End: 123,
Insert: 124,
AndroidForward: 125,
F1: 131,
F2: 132,
F3: 133,
F4: 134,
F5: 135,
F6: 136,
F7: 137,
F8: 138,
F9: 139,
F10: 140,
F11: 141,
F12: 142,
NumLock: 143,
Numpad0: 144,
Numpad1: 145,
Numpad2: 146,
Numpad3: 147,
Numpad4: 148,
Numpad5: 149,
Numpad6: 150,
Numpad7: 151,
Numpad8: 152,
Numpad9: 153,
NumpadDivide: 154,
NumpadMultiply: 155,
NumpadSubtract: 156,
NumpadAdd: 157,
NumpadDecimal: 158,
NumpadComma: 159, // Name not verified
NumpadEnter: 160,
NumpadEquals: 161, // Name not verified
NumpadLeftParen: 162, // Name not verified
NumpadRightParen: 163, // Name not verified
VolumeMute: 164, // Name not verified
AndroidAppSwitch: 187, // Name not verified
AndroidCut: 277,
AndroidCopy: 278,
AndroidPaste: 279,
} as const;
export type AndroidKeyCode =
(typeof AndroidKeyCode)[keyof typeof AndroidKeyCode];
export { AndroidKeyCode };
export const AndroidKeyNames = /* #__PURE__ */ (() =>
Object.fromEntries(

View file

@ -0,0 +1,18 @@
export const InjectKeyCode = 0;
export const InjectText = 1;
export const InjectTouch = 2;
export const InjectScroll = 3;
export const BackOrScreenOn = 4;
export const ExpandNotificationPanel = 5;
export const ExpandSettingPanel = 6;
export const CollapseNotificationPanel = 7;
export const GetClipboard = 8;
export const SetClipboard = 9;
export const SetDisplayPower = 10;
export const RotateDevice = 11;
export const UHidCreate = 12;
export const UHidInput = 13;
export const UHidDestroy = 14;
export const OpenHardKeyboardSettings = 15;
export const StartApp = 16;
export const ResetVideo = 17;

View file

@ -1,24 +1,8 @@
// Their IDs change between versions, so always use `options.getControlMessageTypes()`
export const ScrcpyControlMessageType = {
InjectKeyCode: 0,
InjectText: 1,
InjectTouch: 2,
InjectScroll: 3,
BackOrScreenOn: 4,
ExpandNotificationPanel: 5,
ExpandSettingPanel: 6,
CollapseNotificationPanel: 7,
GetClipboard: 8,
SetClipboard: 9,
SetDisplayPower: 10,
RotateDevice: 11,
UHidCreate: 12,
UHidInput: 13,
UHidDestroy: 14,
OpenHardKeyboardSettings: 15,
StartApp: 16,
ResetVideo: 17,
} as const;
import * as ScrcpyControlMessageType from "./control-message-type-value.js";
export type ScrcpyControlMessageType =
// These IDs change between versions, so always use `options.controlMessageTypes`
// biome-ignore lint/suspicious/noRedeclare: TypeScript declaration merging for enum-like object
type ScrcpyControlMessageType =
(typeof ScrcpyControlMessageType)[keyof typeof ScrcpyControlMessageType];
export { ScrcpyControlMessageType };

View file

@ -18,8 +18,12 @@ import type { ScrcpyMediaStreamPacket } from "./media.js";
import type { ScrcpyScrollController } from "./scroll-controller.js";
import type { ScrcpyVideoStream } from "./video.js";
export type ScrcpyControlMessageTypeMap = Partial<
Record<ScrcpyControlMessageType, number>
>;
export interface ScrcpyOptions<T extends object> {
get controlMessageTypes(): readonly ScrcpyControlMessageType[];
get controlMessageTypes(): ScrcpyControlMessageTypeMap;
value: Required<T>;

View file

@ -1,7 +1,6 @@
export * from "./empty.js";
export * from "./inject-key-code.js";
export * from "./inject-text.js";
export * from "./message-type-map.js";
export * from "./serializer.js";
export * from "./set-screen-power-mode.js";
export * from "./writer.js";

View file

@ -6,19 +6,18 @@ import type {
AndroidKeyEventAction,
AndroidKeyEventMeta,
} from "../android/index.js";
import { ScrcpyControlMessageType } from "../base/index.js";
export const ScrcpyInjectKeyCodeControlMessage = /* #__PURE__ */ (() =>
struct(
{
type: u8(ScrcpyControlMessageType.InjectKeyCode),
action: u8<AndroidKeyEventAction>(),
keyCode: u32<AndroidKeyCode>(),
repeat: u32,
metaState: u32<AndroidKeyEventMeta>(),
},
{ littleEndian: false },
))();
export const ScrcpyInjectKeyCodeControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
action: u8<AndroidKeyEventAction>(),
keyCode: u32<AndroidKeyCode>(),
repeat: u32,
metaState: u32<AndroidKeyEventMeta>(),
},
{ littleEndian: false },
);
export type ScrcpyInjectKeyCodeControlMessage = StructInit<
typeof ScrcpyInjectKeyCodeControlMessage

View file

@ -2,7 +2,11 @@ import type { StructInit } from "@yume-chan/struct";
import { string, struct, u32, u8 } from "@yume-chan/struct";
export const ScrcpyInjectTextControlMessage = struct(
{ type: u8, text: string(u32) },
{
// value of `type` can change between versions
type: u8,
text: string(u32),
},
{ littleEndian: false },
);

View file

@ -1,30 +0,0 @@
import type { ScrcpyControlMessageType, ScrcpyOptions } from "../base/index.js";
/**
* Scrcpy control message types have different values between versions.
*
* This class provides a way to get the actual value for a given type.
*/
export class ScrcpyControlMessageTypeMap {
#types: readonly ScrcpyControlMessageType[];
constructor(options: ScrcpyOptions<object>) {
this.#types = options.controlMessageTypes;
}
get(type: ScrcpyControlMessageType): number {
const value = this.#types.indexOf(type);
if (value === -1) {
throw new TypeError("Invalid or unsupported control message type");
}
return value;
}
fillMessageType<T extends { type: number }>(
message: Omit<T, "type">,
type: ScrcpyControlMessageType,
): T {
(message as T).type = this.get(type);
return message as T;
}
}

View file

@ -14,7 +14,6 @@ import type {
import { EmptyControlMessage } from "./empty.js";
import { ScrcpyInjectKeyCodeControlMessage } from "./inject-key-code.js";
import { ScrcpyInjectTextControlMessage } from "./inject-text.js";
import { ScrcpyControlMessageTypeMap } from "./message-type-map.js";
import { ScrcpySetDisplayPowerControlMessage } from "./set-screen-power-mode.js";
import { ScrcpyStartAppControlMessage } from "./start-app.js";
import {
@ -24,28 +23,39 @@ import {
export class ScrcpyControlMessageSerializer {
#options: ScrcpyOptions<object>;
#typeMap: ScrcpyControlMessageTypeMap;
#scrollController: ScrcpyScrollController;
constructor(options: ScrcpyOptions<object>) {
this.#options = options;
this.#typeMap = new ScrcpyControlMessageTypeMap(options);
this.#scrollController = options.createScrollController();
}
getType(type: ScrcpyControlMessageType): number {
const value = this.#options.controlMessageTypes[type];
if (value === undefined) {
throw new TypeError(`Invalid control message type: ${type}`);
}
return value;
}
#addType<T extends { type: number }>(
message: Omit<T, "type">,
type: ScrcpyControlMessageType,
): T {
(message as T).type = this.getType(type);
return message as T;
}
injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, "type">) {
return ScrcpyInjectKeyCodeControlMessage.serialize(
this.#typeMap.fillMessageType(
message,
ScrcpyControlMessageType.InjectKeyCode,
),
this.#addType(message, ScrcpyControlMessageType.InjectKeyCode),
);
}
injectText(text: string) {
return ScrcpyInjectTextControlMessage.serialize({
text,
type: this.#typeMap.get(ScrcpyControlMessageType.InjectText),
type: this.getType(ScrcpyControlMessageType.InjectText),
});
}
@ -54,10 +64,7 @@ export class ScrcpyControlMessageSerializer {
*/
injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, "type">) {
return this.#options.serializeInjectTouchControlMessage(
this.#typeMap.fillMessageType(
message,
ScrcpyControlMessageType.InjectTouch,
),
this.#addType(message, ScrcpyControlMessageType.InjectTouch),
);
}
@ -66,30 +73,27 @@ export class ScrcpyControlMessageSerializer {
*/
injectScroll(message: Omit<ScrcpyInjectScrollControlMessage, "type">) {
return this.#scrollController.serializeScrollMessage(
this.#typeMap.fillMessageType(
message,
ScrcpyControlMessageType.InjectScroll,
),
this.#addType(message, ScrcpyControlMessageType.InjectScroll),
);
}
backOrScreenOn(action: AndroidKeyEventAction) {
return this.#options.serializeBackOrScreenOnControlMessage({
action,
type: this.#typeMap.get(ScrcpyControlMessageType.BackOrScreenOn),
type: this.getType(ScrcpyControlMessageType.BackOrScreenOn),
});
}
setDisplayPower(mode: AndroidScreenPowerMode) {
return ScrcpySetDisplayPowerControlMessage.serialize({
mode,
type: this.#typeMap.get(ScrcpyControlMessageType.SetDisplayPower),
type: this.getType(ScrcpyControlMessageType.SetDisplayPower),
});
}
expandNotificationPanel() {
return EmptyControlMessage.serialize({
type: this.#typeMap.get(
type: this.getType(
ScrcpyControlMessageType.ExpandNotificationPanel,
),
});
@ -97,15 +101,13 @@ export class ScrcpyControlMessageSerializer {
expandSettingPanel() {
return EmptyControlMessage.serialize({
type: this.#typeMap.get(
ScrcpyControlMessageType.ExpandSettingPanel,
),
type: this.getType(ScrcpyControlMessageType.ExpandSettingPanel),
});
}
collapseNotificationPanel() {
return EmptyControlMessage.serialize({
type: this.#typeMap.get(
type: this.getType(
ScrcpyControlMessageType.CollapseNotificationPanel,
),
});
@ -113,14 +115,14 @@ export class ScrcpyControlMessageSerializer {
rotateDevice() {
return EmptyControlMessage.serialize({
type: this.#typeMap.get(ScrcpyControlMessageType.RotateDevice),
type: this.getType(ScrcpyControlMessageType.RotateDevice),
});
}
setClipboard(message: Omit<ScrcpySetClipboardControlMessage, "type">) {
return this.#options.serializeSetClipboardControlMessage({
...message,
type: this.#typeMap.get(ScrcpyControlMessageType.SetClipboard),
type: this.getType(ScrcpyControlMessageType.SetClipboard),
});
}
@ -130,25 +132,19 @@ export class ScrcpyControlMessageSerializer {
}
return this.#options.serializeUHidCreateControlMessage(
this.#typeMap.fillMessageType(
message,
ScrcpyControlMessageType.UHidCreate,
),
this.#addType(message, ScrcpyControlMessageType.UHidCreate),
);
}
uHidInput(message: Omit<ScrcpyUHidInputControlMessage, "type">) {
return ScrcpyUHidInputControlMessage.serialize(
this.#typeMap.fillMessageType(
message,
ScrcpyControlMessageType.UHidInput,
),
this.#addType(message, ScrcpyControlMessageType.UHidInput),
);
}
uHidDestroy(id: number) {
return ScrcpyUHidDestroyControlMessage.serialize({
type: this.#typeMap.get(ScrcpyControlMessageType.UHidDestroy),
type: this.getType(ScrcpyControlMessageType.UHidDestroy),
id,
});
}
@ -165,14 +161,14 @@ export class ScrcpyControlMessageSerializer {
}
return ScrcpyStartAppControlMessage.serialize({
type: this.#typeMap.get(ScrcpyControlMessageType.StartApp),
type: this.getType(ScrcpyControlMessageType.StartApp),
name,
});
}
resetVideo() {
return EmptyControlMessage.serialize({
type: this.#typeMap.get(ScrcpyControlMessageType.ResetVideo),
type: this.getType(ScrcpyControlMessageType.ResetVideo),
});
}
}

View file

@ -4,7 +4,11 @@ import { struct, u8 } from "@yume-chan/struct";
import type { AndroidScreenPowerMode } from "../android/index.js";
export const ScrcpySetDisplayPowerControlMessage = struct(
{ type: u8, mode: u8<AndroidScreenPowerMode>() },
{
// value of `type` can change between versions
type: u8,
mode: u8<AndroidScreenPowerMode>(),
},
{ littleEndian: false },
);

View file

@ -3,6 +3,7 @@ import { string, struct, u8 } from "@yume-chan/struct";
export const ScrcpyStartAppControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
name: string(u8),
},

View file

@ -1,24 +1,26 @@
import type { StructInit } from "@yume-chan/struct";
import { buffer, struct, u16, u8 } from "@yume-chan/struct";
import { ScrcpyControlMessageType } from "../base/index.js";
export const ScrcpyUHidInputControlMessage = /* #__PURE__ */ (() =>
struct(
{
type: u8(ScrcpyControlMessageType.UHidInput),
id: u16,
data: buffer(u16),
},
{ littleEndian: false },
))();
export const ScrcpyUHidInputControlMessage = struct(
{
// value of `type` can change between versions
type: u8,
id: u16,
data: buffer(u16),
},
{ littleEndian: false },
);
export type ScrcpyUHidInputControlMessage = StructInit<
typeof ScrcpyUHidInputControlMessage
>;
export const ScrcpyUHidDestroyControlMessage = struct(
{ type: u8, id: u16 },
{
// value of `type` can change between versions
type: u8,
id: u16,
},
{ littleEndian: false },
);

View file

@ -1,17 +1,4 @@
import { ScrcpyOptions3_3_2 } from "./3_3_2.js";
export class ScrcpyOptionsLatest<
TVideo extends boolean,
> extends ScrcpyOptions3_3_2<TVideo> {
constructor(init: ScrcpyOptions3_3_2.Init<TVideo>) {
super(init);
}
}
export namespace ScrcpyOptionsLatest {
export type Init<TVideo extends boolean = boolean> =
ScrcpyOptions3_3_2.Init<TVideo>;
}
export { ScrcpyOptions3_3_2 as ScrcpyOptionsLatest } from "./3_3_2.js";
export {
BackOrScreenOnControlMessage as ScrcpyBackOrScreenOnControlMessage,

View file

@ -24,7 +24,7 @@ export type BipedalGenerator<This, T, A extends unknown[]> = (
this: This,
then: <U>(value: MaybePromiseLike<U>) => Iterable<unknown, U, unknown>,
...args: A
) => Generator<unknown, T, unknown>;
) => Generator<unknown, MaybePromiseLike<T>, unknown>;
/* #__NO_SIDE_EFFECTS__ */
export function bipedal<This, T, A extends unknown[]>(

View file

@ -1,22 +1,26 @@
import type { Field } from "./field/index.js";
import type {
BipedalFieldDeserializer,
ByobFieldSerializer,
Field,
} from "./field/index.js";
import { field } from "./field/index.js";
export const EmptyUint8Array = new Uint8Array(0);
function copyMaybeDifferentLength(
dist: Uint8Array,
dest: Uint8Array,
source: Uint8Array,
index: number,
length: number,
) {
if (source.length < length) {
dist.set(source, index);
dest.set(source, index);
// Clear trailing bytes
dist.fill(0, index + source.length, index + length);
dest.fill(0, index + source.length, index + length);
} else if (source.length === length) {
dist.set(source, index);
dest.set(source, index);
} else {
dist.set(source.subarray(0, length), index);
dest.set(source.subarray(0, length), index);
}
}
@ -29,70 +33,97 @@ export interface BufferLengthConverter<K, KT> extends Converter<KT, number> {
field: K;
}
export interface BufferLike {
(length: number): Field<Uint8Array, never, never, Uint8Array>;
<U>(
length: number,
converter: Converter<Uint8Array, U>,
): Field<U, never, never, Uint8Array>;
<K extends string>(
lengthField: K,
): Field<Uint8Array, K, Record<K, number>, Uint8Array>;
<K extends string, U>(
lengthField: K,
converter: Converter<Uint8Array, U>,
): Field<U, K, Record<K, number>, Uint8Array>;
<K extends string, KT>(
length: BufferLengthConverter<K, KT>,
): Field<Uint8Array, K, Record<K, KT>, Uint8Array>;
<K extends string, KT, U>(
length: BufferLengthConverter<K, KT>,
converter: Converter<Uint8Array, U>,
): Field<U, K, Record<K, KT>, Uint8Array>;
<LengthOmitInit extends string, LengthDependencies>(
length: Field<number, LengthOmitInit, LengthDependencies, number>,
): Field<Uint8Array, LengthOmitInit, LengthDependencies, Uint8Array>;
<LengthOmitInit extends string, LengthDependencies, U>(
length: Field<number, LengthOmitInit, LengthDependencies, number>,
converter: Converter<Uint8Array, U>,
): Field<U, LengthOmitInit, LengthDependencies, Uint8Array>;
}
function _buffer(length: number): Field<Uint8Array, never, never, Uint8Array>;
function _buffer<U>(
/**
* Create a fixed-length `Uint8Array` field.
*
* @param length Length of the field
*/
export function buffer(
length: number,
): Field<Uint8Array, never, never, Uint8Array>;
/**
* Create a custom-typed field, backed by a fixed-length `Uint8Array`.
*
* @param length Length of the field
* @param converter A value converter to convert between `Uint8Array` and the target type
*/
export function buffer<U>(
length: number,
converter: Converter<Uint8Array, U>,
): Field<U, never, never, Uint8Array>;
function _buffer<K extends string>(
/**
* Create a variable-length `Uint8Array` field.
* The length is determined by another number-typed field.
*
* @param lengthField Name of the length field. Must be declared before this field
*/
export function buffer<K extends string>(
lengthField: K,
): Field<Uint8Array, K, Record<K, number>, Uint8Array>;
function _buffer<K extends string, U>(
/**
* Create a custom-typed field, backed by a variable-length `Uint8Array`.
* The length is determined by another number-typed field.
*
* @param lengthField Name of the length field. Must be declared before this field
* @param converter A value converter to convert between `Uint8Array` and the target type
*/
export function buffer<K extends string, U>(
lengthField: K,
converter: Converter<Uint8Array, U>,
): Field<U, K, Record<K, number>, Uint8Array>;
function _buffer<K extends string, KT>(
/**
* Create a variable-length `Uint8Array` field.
* The length is determined by converting another field to `number`.
*
* @param length
* Name of the length field,
* and a converter to convert between source type and `number`.
* Must be declared before this field
*/
export function buffer<K extends string, KT>(
length: BufferLengthConverter<K, KT>,
): Field<Uint8Array, K, Record<K, KT>, Uint8Array>;
function _buffer<K extends string, KT, U>(
/**
* Create a custom-typed field, backed by a variable-length `Uint8Array`.
* The length is determined by converting another field to `number`.
*
* @param length
* Name of the length field,
* and a converter to convert between source type and `number`.
* Must be declared before this field
* @param converter
* A value converter to convert between `Uint8Array` and the target type
*/
export function buffer<K extends string, KT, U>(
length: BufferLengthConverter<K, KT>,
converter: Converter<Uint8Array, U>,
): Field<U, K, Record<K, KT>, Uint8Array>;
function _buffer<LengthOmitInit extends string, LengthDependencies>(
/**
* Create a length field, and a variable-length `Uint8Array` field.
* This is a shortcut when the length field is directly before the data field.
*
* @param length The length field declaration
*/
export function buffer<LengthOmitInit extends string, LengthDependencies>(
length: Field<number, LengthOmitInit, LengthDependencies, number>,
): Field<Uint8Array, LengthOmitInit, LengthDependencies, Uint8Array>;
function _buffer<LengthOmitInit extends string, LengthDependencies, U>(
/**
* Create a length field, and a custom-typed field, backed by a variable-length `Uint8Array`.
* This is a shortcut when the length field is directly before the data field.
*
* @param length The length field declaration
* @param converter A value converter to convert between `Uint8Array` and the target type
*/
export function buffer<LengthOmitInit extends string, LengthDependencies, U>(
length: Field<number, LengthOmitInit, LengthDependencies, number>,
converter: Converter<Uint8Array, U>,
): Field<U, LengthOmitInit, LengthDependencies, Uint8Array>;
/* #__NO_SIDE_EFFECTS__ */
function _buffer(
export function buffer(
lengthOrField:
| string
| number
@ -102,67 +133,47 @@ function _buffer(
): Field<unknown, string, Record<string, unknown>, Uint8Array> {
// Fixed length
if (typeof lengthOrField === "number") {
if (converter) {
if (lengthOrField === 0) {
return field(
0,
"byob",
() => {},
// eslint-disable-next-line require-yield
function* () {
return converter.convert(EmptyUint8Array);
},
);
}
return field(
lengthOrField,
"byob",
(value, { buffer, index }) => {
copyMaybeDifferentLength(
buffer,
value,
index,
lengthOrField,
);
},
function* (then, reader) {
const array = yield* then(
reader.readExactly(lengthOrField),
);
return converter.convert(array);
},
{
init(value) {
return converter.back(value);
},
},
);
}
let serialize: ByobFieldSerializer<Uint8Array>;
let deserialize: BipedalFieldDeserializer<
unknown,
Record<string, unknown>
>;
let init: ((value: unknown) => Uint8Array) | undefined;
if (lengthOrField === 0) {
return field(
0,
"byob",
() => {},
serialize = () => {};
if (converter) {
// eslint-disable-next-line require-yield
function* () {
deserialize = function* () {
return converter.convert(EmptyUint8Array);
};
} else {
// eslint-disable-next-line require-yield
deserialize = function* () {
return EmptyUint8Array;
},
);
};
}
} else {
serialize = (value, { buffer, index }) =>
copyMaybeDifferentLength(buffer, value, index, lengthOrField);
if (converter) {
deserialize = function* (then, reader) {
const array = reader.readExactly(lengthOrField);
return converter.convert(yield* then(array));
};
init = (value) => converter.back(value);
} else {
// eslint-disable-next-line require-yield
deserialize = function* (_then, reader) {
const array = reader.readExactly(lengthOrField);
return array;
};
}
}
return field(
lengthOrField,
"byob",
(value, { buffer, index }) => {
copyMaybeDifferentLength(buffer, value, index, lengthOrField);
},
// eslint-disable-next-line require-yield
function* (_then, reader) {
return reader.readExactly(lengthOrField);
},
);
return field(lengthOrField, "byob", serialize, deserialize, { init });
}
// Declare length field
@ -172,48 +183,31 @@ function _buffer(
typeof lengthOrField === "function") &&
"serialize" in lengthOrField
) {
let deserialize: BipedalFieldDeserializer<
unknown,
Record<string, unknown>
>;
let init: ((value: unknown) => Uint8Array) | undefined;
if (converter) {
return field(
lengthOrField.size,
"default",
(value, { littleEndian }) => {
if (lengthOrField.type === "default") {
const lengthBuffer = lengthOrField.serialize(
value.length,
{ littleEndian },
);
const result = new Uint8Array(
lengthBuffer.length + value.length,
);
result.set(lengthBuffer, 0);
result.set(value, lengthBuffer.length);
return result;
} else {
const result = new Uint8Array(
lengthOrField.size + value.length,
);
lengthOrField.serialize(value.length, {
buffer: result,
index: 0,
littleEndian,
});
result.set(value, lengthOrField.size);
return result;
}
},
function* (then, reader, context) {
const length = yield* then(
lengthOrField.deserialize(reader, context),
);
const array = yield* then(reader.readExactly(length));
return converter.convert(array);
},
{
init(value) {
return converter.back(value);
},
},
);
deserialize = function* (then, reader, context) {
const length = yield* then(
lengthOrField.deserialize(reader, context),
);
const array =
length !== 0 ? reader.readExactly(length) : EmptyUint8Array;
return converter.convert(yield* then(array));
};
init = (value) => converter.back(value);
} else {
deserialize = function* (then, reader, context) {
const length = yield* then(
lengthOrField.deserialize(reader, context),
);
const array =
length !== 0 ? reader.readExactly(length) : EmptyUint8Array;
return array;
};
}
return field(
@ -224,6 +218,11 @@ function _buffer(
const lengthBuffer = lengthOrField.serialize(value.length, {
littleEndian,
});
if (value.length === 0) {
return lengthBuffer;
}
const result = new Uint8Array(
lengthBuffer.length + value.length,
);
@ -243,114 +242,91 @@ function _buffer(
return result;
}
},
function* (then, reader, context) {
const length = yield* then(
lengthOrField.deserialize(reader, context),
);
return yield* then(reader.readExactly(length));
},
deserialize,
{ init },
);
}
// Reference exiting length field
// Reference existing length field
if (typeof lengthOrField === "string") {
if (converter) {
return field(
0,
"default",
(source) => source,
// eslint-disable-next-line require-yield
function* (_then, reader, { dependencies }) {
const length = dependencies[lengthOrField] as number;
if (length === 0) {
return EmptyUint8Array;
}
let deserialize: BipedalFieldDeserializer<
unknown,
Record<string, unknown>
>;
let init: (
value: unknown,
dependencies: Record<string, unknown>,
) => Uint8Array;
return reader.readExactly(length);
},
{
init(value, dependencies) {
const array = converter.back(value);
dependencies[lengthOrField] = array.length;
return array;
},
},
);
if (converter) {
deserialize = function* (then, reader, { dependencies }) {
const length = dependencies[lengthOrField] as number;
const array =
length !== 0 ? reader.readExactly(length) : EmptyUint8Array;
return converter.convert(yield* then(array));
};
init = (value, dependencies) => {
const array = converter.back(value);
dependencies[lengthOrField] = array.length;
return array;
};
} else {
// eslint-disable-next-line require-yield
deserialize = function* (_then, reader, { dependencies }) {
const length = dependencies[lengthOrField] as number;
const array =
length !== 0 ? reader.readExactly(length) : EmptyUint8Array;
return array;
};
init = (value, dependencies) => {
const array = value as Uint8Array;
dependencies[lengthOrField] = array.length;
return array;
};
}
return field(
0,
"default",
(source) => source,
// eslint-disable-next-line require-yield
function* (_then, reader, { dependencies }) {
const length = dependencies[lengthOrField] as number;
if (length === 0) {
return EmptyUint8Array;
}
return reader.readExactly(length);
},
{
init(value, dependencies) {
dependencies[lengthOrField] = (value as Uint8Array).length;
return undefined;
},
},
);
return field(0, "default", (source) => source, deserialize, { init });
}
// Reference existing length field + converter
let deserialize: BipedalFieldDeserializer<unknown, Record<string, unknown>>;
let init: (
value: unknown,
dependencies: Record<string, unknown>,
) => Uint8Array;
// Reference existing length field + length converter
if (converter) {
return field(
0,
"default",
(source) => source,
// eslint-disable-next-line require-yield
function* (_then, reader, { dependencies }) {
const rawLength = dependencies[lengthOrField.field];
const length = lengthOrField.convert(rawLength);
if (length === 0) {
return EmptyUint8Array;
}
return reader.readExactly(length);
},
{
init(value, dependencies) {
const array = converter.back(value);
dependencies[lengthOrField.field] = lengthOrField.back(
array.length,
);
return array;
},
},
);
}
return field(
0,
"default",
(source) => source,
// eslint-disable-next-line require-yield
function* (_then, reader, { dependencies }) {
deserialize = function* (then, reader, { dependencies }) {
const rawLength = dependencies[lengthOrField.field];
const length = lengthOrField.convert(rawLength);
if (length === 0) {
return EmptyUint8Array;
}
const array =
length !== 0 ? reader.readExactly(length) : EmptyUint8Array;
return converter.convert(yield* then(array));
};
init = (value, dependencies) => {
const array = converter.back(value);
dependencies[lengthOrField.field] = lengthOrField.back(
array.length,
);
return array;
};
} else {
// eslint-disable-next-line require-yield
deserialize = function* (_then, reader, { dependencies }) {
const rawLength = dependencies[lengthOrField.field];
const length = lengthOrField.convert(rawLength);
const array =
length !== 0 ? reader.readExactly(length) : EmptyUint8Array;
return array;
};
init = (value, dependencies) => {
const array = value as Uint8Array;
dependencies[lengthOrField.field] = lengthOrField.back(
array.length,
);
return array;
};
}
return reader.readExactly(length);
},
{
init(value, dependencies) {
dependencies[lengthOrField.field] = lengthOrField.back(
(value as Uint8Array).length,
);
return undefined;
},
},
);
return field(0, "default", (source) => source, deserialize, { init });
}
export const buffer = _buffer;

View file

@ -25,7 +25,10 @@ export interface Field<T, OmitInit extends string, D, Raw = T>
FieldDeserializer<T, D> {
omitInit: OmitInit | undefined;
init?(value: T, dependencies: D): Raw | undefined;
/**
* A function to convert deserialized value back to raw value for serialization.
*/
init?(value: T, dependencies: D): Raw;
}
export interface FieldDeserializeContext<D> {
@ -44,5 +47,8 @@ export interface FieldDeserializer<T, D> {
export interface FieldOptions<T, OmitInit extends string, D, Raw = T> {
omitInit?: OmitInit;
dependencies?: D;
init?: (value: T, dependencies: D) => Raw | undefined;
/**
* A function to convert deserialized value back to raw value for serialization.
*/
init?: ((value: T, dependencies: D) => Raw) | undefined;
}

View file

@ -132,9 +132,7 @@ export function struct<
for (const [key, field] of fieldList) {
if (key in temp && "init" in field) {
const result = field.init?.(temp[key], temp as never);
if (result !== undefined) {
temp[key] = result;
}
temp[key] = result;
}
}

View file

@ -0,0 +1,3 @@
# Side effect test
Use Rollup's `experimentalLogSideEffects` to log any side effects in exports.

View file

@ -0,0 +1,34 @@
import { writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
async function generateTest(packageName, filename) {
const exports = await import(packageName);
const names = Object.keys(exports)
.filter(
(name) =>
name !== "default" && name !== "__esModule" && name !== "then",
)
.sort();
writeFileSync(
resolve(dirname(fileURLToPath(import.meta.url)), filename),
`
// Generated by toolchain/side-effect-test/generate.mjs
// DO NOT MODIFY THIS FILE MANUALLY
/* eslint-disable */
import { ${names.join(", ")} } from "${packageName}";
export default () => {
console.log(${names.join(", ")})
}
`,
"utf8",
);
}
await Promise.all([
generateTest("@yume-chan/scrcpy", "./src/scrcpy.js"),
generateTest("@yume-chan/adb", "./src/adb.js"),
]);

View file

@ -1,4 +1,5 @@
import node from "@rollup/plugin-node-resolve";
import terser from "@rollup/plugin-terser";
import typescript from "@rollup/plugin-typescript";
import { defineConfig } from "rollup";
@ -13,11 +14,15 @@ export default defineConfig({
plugins: [
typescript(),
node(),
// terser({
// module: true,
// format: {
// beautify: true,
// },
// }),
terser({
module: true,
format: {
beautify: true,
},
compress: {
passes: 10,
},
mangle: false,
}),
],
});

View file

@ -0,0 +1,10 @@
// Generated by toolchain/side-effect-test/generate.mjs
// DO NOT MODIFY THIS FILE MANUALLY
/* eslint-disable */
import { ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE, ADB_DAEMON_VERSION_OMIT_CHECKSUM, ADB_SYNC_MAX_PACKET_SIZE, ASN1_NULL, ASN1_OCTET_STRING, ASN1_OID, ASN1_SEQUENCE, Adb, AdbAuthType, AdbBanner, AdbBannerKey, AdbCommand, AdbDaemonSocket, AdbDaemonSocketController, AdbDaemonTransport, AdbDefaultAuthenticator, AdbDeviceFeatures, AdbFeature, AdbFrameBufferError, AdbFrameBufferForbiddenError, AdbFrameBufferUnsupportedVersionError, AdbFrameBufferV1, AdbFrameBufferV2, AdbNoneProtocolProcessImpl, AdbNoneProtocolPtyProcess, AdbNoneProtocolSubprocessService, AdbPacket, AdbPacketDispatcher, AdbPacketHeader, AdbPacketSerializeStream, AdbPower, AdbReverseError, AdbReverseNotSupportedError, AdbReverseService, AdbServerClient, AdbServerDeviceObserverOwner, AdbServerStream, AdbServerTransport, AdbServiceBase, AdbShellProtocolId, AdbShellProtocolPacket, AdbShellProtocolProcessImpl, AdbShellProtocolPtyProcess, AdbShellProtocolSubprocessService, AdbSubprocessService, AdbSync, AdbSyncDataResponse, AdbSyncEntry2Response, AdbSyncEntryResponse, AdbSyncError, AdbSyncFailResponse, AdbSyncLstatResponse, AdbSyncNumberRequest, AdbSyncOkResponse, AdbSyncRequestId, AdbSyncResponseId, AdbSyncSendV2Flags, AdbSyncSendV2Request, AdbSyncSocket, AdbSyncSocketLocked, AdbSyncStatErrorCode, AdbSyncStatResponse, AdbTcpIpService, AutoResetEvent, FAIL, LinuxFileType, NOOP, Ref, SHA1_DIGEST_INFO, SHA1_DIGEST_LENGTH, ToArrayStream, adbGeneratePublicKey, adbGetPublicKeySize, adbNoneProtocolSpawner, adbShellProtocolSpawner, adbSyncLstat, adbSyncOpenDir, adbSyncOpenDirV1, adbSyncOpenDirV2, adbSyncPull, adbSyncPullGenerator, adbSyncPush, adbSyncPushV1, adbSyncPushV2, adbSyncReadResponse, adbSyncReadResponses, adbSyncStat, adbSyncWriteRequest, calculateBase64EncodedLength, calculateChecksum, createLazyPromise, decodeBase64, decodeUtf8, decodeUtf8Chunked, dirname, encodeBase64, encodeUtf8, escapeArg, framebuffer, getBigUint, hexToNumber, modInverse, powMod, raceSignal, rsaParsePrivateKey, rsaSign, sequenceEqual, setBigUint, splitCommand, toLocalUint8Array, unorderedRemove, unreachable, write4HexDigits } from "@yume-chan/adb";
export default () => {
console.log(ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE, ADB_DAEMON_VERSION_OMIT_CHECKSUM, ADB_SYNC_MAX_PACKET_SIZE, ASN1_NULL, ASN1_OCTET_STRING, ASN1_OID, ASN1_SEQUENCE, Adb, AdbAuthType, AdbBanner, AdbBannerKey, AdbCommand, AdbDaemonSocket, AdbDaemonSocketController, AdbDaemonTransport, AdbDefaultAuthenticator, AdbDeviceFeatures, AdbFeature, AdbFrameBufferError, AdbFrameBufferForbiddenError, AdbFrameBufferUnsupportedVersionError, AdbFrameBufferV1, AdbFrameBufferV2, AdbNoneProtocolProcessImpl, AdbNoneProtocolPtyProcess, AdbNoneProtocolSubprocessService, AdbPacket, AdbPacketDispatcher, AdbPacketHeader, AdbPacketSerializeStream, AdbPower, AdbReverseError, AdbReverseNotSupportedError, AdbReverseService, AdbServerClient, AdbServerDeviceObserverOwner, AdbServerStream, AdbServerTransport, AdbServiceBase, AdbShellProtocolId, AdbShellProtocolPacket, AdbShellProtocolProcessImpl, AdbShellProtocolPtyProcess, AdbShellProtocolSubprocessService, AdbSubprocessService, AdbSync, AdbSyncDataResponse, AdbSyncEntry2Response, AdbSyncEntryResponse, AdbSyncError, AdbSyncFailResponse, AdbSyncLstatResponse, AdbSyncNumberRequest, AdbSyncOkResponse, AdbSyncRequestId, AdbSyncResponseId, AdbSyncSendV2Flags, AdbSyncSendV2Request, AdbSyncSocket, AdbSyncSocketLocked, AdbSyncStatErrorCode, AdbSyncStatResponse, AdbTcpIpService, AutoResetEvent, FAIL, LinuxFileType, NOOP, Ref, SHA1_DIGEST_INFO, SHA1_DIGEST_LENGTH, ToArrayStream, adbGeneratePublicKey, adbGetPublicKeySize, adbNoneProtocolSpawner, adbShellProtocolSpawner, adbSyncLstat, adbSyncOpenDir, adbSyncOpenDirV1, adbSyncOpenDirV2, adbSyncPull, adbSyncPullGenerator, adbSyncPush, adbSyncPushV1, adbSyncPushV2, adbSyncReadResponse, adbSyncReadResponses, adbSyncStat, adbSyncWriteRequest, calculateBase64EncodedLength, calculateChecksum, createLazyPromise, decodeBase64, decodeUtf8, decodeUtf8Chunked, dirname, encodeBase64, encodeUtf8, escapeArg, framebuffer, getBigUint, hexToNumber, modInverse, powMod, raceSignal, rsaParsePrivateKey, rsaSign, sequenceEqual, setBigUint, splitCommand, toLocalUint8Array, unorderedRemove, unreachable, write4HexDigits)
}

View file

@ -1,42 +1,2 @@
import {
bipedal,
buffer,
concat,
decodeUtf8,
encodeUtf8,
extend,
s16,
s32,
s64,
s8,
string,
struct,
u16,
u32,
u64,
u8,
} from "@yume-chan/struct";
bipedal(function* () {});
buffer(u8);
decodeUtf8(new Uint8Array());
encodeUtf8("");
s16(1);
s32(1);
s64(1);
s8(1);
string(1);
u16(1);
u32(1);
u64(1);
u8(1);
struct({}, { littleEndian: true });
concat(
{ littleEndian: true },
struct({ a: u8 }, { littleEndian: true }),
struct({ b: u8 }, { littleEndian: true }),
);
extend(struct({ a: u16 }, { littleEndian: true }), { b: buffer(32) });
export * from "@yume-chan/scrcpy";
export * from "@yume-chan/struct";
export { default as Adb } from "./adb.js";
export { default as Scrcpy } from "./scrcpy.js";

View file

@ -0,0 +1,10 @@
// Generated by toolchain/side-effect-test/generate.mjs
// DO NOT MODIFY THIS FILE MANUALLY
/* eslint-disable */
import { AndroidAvcLevel, AndroidAvcProfile, AndroidHevcLevel, AndroidHevcProfile, AndroidKeyCode, AndroidKeyEventAction, AndroidKeyEventMeta, AndroidKeyNames, AndroidMotionEventAction, AndroidMotionEventButton, AndroidScreenPowerMode, DefaultServerPath, EmptyControlMessage, ScrcpyAudioCodec, ScrcpyBackOrScreenOnControlMessage, ScrcpyCaptureOrientation, ScrcpyCodecOptions, ScrcpyControlMessageSerializer, ScrcpyControlMessageType, ScrcpyControlMessageWriter, ScrcpyCrop, ScrcpyDeviceMessageParsers, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectScrollControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpyInstanceId, ScrcpyLockOrientation, ScrcpyNewDisplay, ScrcpyOptions1_15, ScrcpyOptions1_15_1, ScrcpyOptions1_16, ScrcpyOptions1_17, ScrcpyOptions1_18, ScrcpyOptions1_19, ScrcpyOptions1_20, ScrcpyOptions1_21, ScrcpyOptions1_22, ScrcpyOptions1_23, ScrcpyOptions1_24, ScrcpyOptions1_25, ScrcpyOptions2_0, ScrcpyOptions2_1, ScrcpyOptions2_1_1, ScrcpyOptions2_2, ScrcpyOptions2_3, ScrcpyOptions2_3_1, ScrcpyOptions2_4, ScrcpyOptions2_5, ScrcpyOptions2_6, ScrcpyOptions2_6_1, ScrcpyOptions2_7, ScrcpyOptions3_0, ScrcpyOptions3_0_1, ScrcpyOptions3_0_2, ScrcpyOptions3_1, ScrcpyOptions3_2, ScrcpyOptions3_3, ScrcpyOptions3_3_1, ScrcpyOptions3_3_2, ScrcpyOptionsLatest, ScrcpyOrientation, ScrcpyPointerId, ScrcpySetClipboardControlMessage, ScrcpySetDisplayPowerControlMessage, ScrcpyUHidCreateControlMessage, ScrcpyUHidOutputDeviceMessage, ScrcpyVideoCodecId, ScrcpyVideoCodecNameMap, ScrcpyVideoSizeImpl, clamp, isScrcpyOptionValue, omit, toScrcpyOptionValue } from "@yume-chan/scrcpy";
export default () => {
console.log(AndroidAvcLevel, AndroidAvcProfile, AndroidHevcLevel, AndroidHevcProfile, AndroidKeyCode, AndroidKeyEventAction, AndroidKeyEventMeta, AndroidKeyNames, AndroidMotionEventAction, AndroidMotionEventButton, AndroidScreenPowerMode, DefaultServerPath, EmptyControlMessage, ScrcpyAudioCodec, ScrcpyBackOrScreenOnControlMessage, ScrcpyCaptureOrientation, ScrcpyCodecOptions, ScrcpyControlMessageSerializer, ScrcpyControlMessageType, ScrcpyControlMessageWriter, ScrcpyCrop, ScrcpyDeviceMessageParsers, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectScrollControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpyInstanceId, ScrcpyLockOrientation, ScrcpyNewDisplay, ScrcpyOptions1_15, ScrcpyOptions1_15_1, ScrcpyOptions1_16, ScrcpyOptions1_17, ScrcpyOptions1_18, ScrcpyOptions1_19, ScrcpyOptions1_20, ScrcpyOptions1_21, ScrcpyOptions1_22, ScrcpyOptions1_23, ScrcpyOptions1_24, ScrcpyOptions1_25, ScrcpyOptions2_0, ScrcpyOptions2_1, ScrcpyOptions2_1_1, ScrcpyOptions2_2, ScrcpyOptions2_3, ScrcpyOptions2_3_1, ScrcpyOptions2_4, ScrcpyOptions2_5, ScrcpyOptions2_6, ScrcpyOptions2_6_1, ScrcpyOptions2_7, ScrcpyOptions3_0, ScrcpyOptions3_0_1, ScrcpyOptions3_0_2, ScrcpyOptions3_1, ScrcpyOptions3_2, ScrcpyOptions3_3, ScrcpyOptions3_3_1, ScrcpyOptions3_3_2, ScrcpyOptionsLatest, ScrcpyOrientation, ScrcpyPointerId, ScrcpySetClipboardControlMessage, ScrcpySetDisplayPowerControlMessage, ScrcpyUHidCreateControlMessage, ScrcpyUHidOutputDeviceMessage, ScrcpyVideoCodecId, ScrcpyVideoCodecNameMap, ScrcpyVideoSizeImpl, clamp, isScrcpyOptionValue, omit, toScrcpyOptionValue)
}

View file

@ -0,0 +1,39 @@
import {
bipedal,
buffer,
concat,
decodeUtf8,
encodeUtf8,
extend,
s16,
s32,
s64,
s8,
string,
struct,
u16,
u32,
u64,
u8,
} from "@yume-chan/struct";
bipedal(function* () {});
buffer(u8);
decodeUtf8(new Uint8Array());
encodeUtf8("");
s16(1);
s32(1);
s64(1);
s8(1);
string(1);
u16(1);
u32(1);
u64(1);
u8(1);
struct({}, { littleEndian: true });
concat(
{ littleEndian: true },
struct({ a: u8 }, { littleEndian: true }),
struct({ b: u8 }, { littleEndian: true }),
);
extend(struct({ a: u16 }, { littleEndian: true }), { b: buffer(32) });