mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-04 02:09:18 +02:00
refactor(scrcpy): rewrite option classes to improve tree-shaking
This commit is contained in:
parent
92472007db
commit
cc5d52912e
63 changed files with 595 additions and 325 deletions
|
@ -46,6 +46,8 @@ export class AdbScrcpyOptions1_16 extends AdbScrcpyOptions<
|
|||
const client = await AdbScrcpyClient.start(adb, path, version, options);
|
||||
|
||||
const encoders: ScrcpyEncoder[] = [];
|
||||
|
||||
// `client.stdout` is supplied by user and may not support async iteration
|
||||
await client.stdout.pipeTo(
|
||||
new WritableStream({
|
||||
write: (line) => {
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { MaybeConsumable, WritableStream } from "@yume-chan/stream-extra";
|
|||
import { ReadableStream } from "@yume-chan/stream-extra";
|
||||
|
||||
import type { Adb, AdbSocket } from "../../../adb.js";
|
||||
import { unreachable } from "../../../utils/index.js";
|
||||
|
||||
import type { AdbSubprocessProtocol } from "./types.js";
|
||||
|
||||
|
@ -64,10 +63,9 @@ export class AdbSubprocessNoneProtocol implements AdbSubprocessProtocol {
|
|||
this.#socket = socket;
|
||||
|
||||
this.#stderr = new ReadableStream({
|
||||
start: (controller) => {
|
||||
this.#socket.closed
|
||||
.then(() => controller.close())
|
||||
.catch(unreachable);
|
||||
start: async (controller) => {
|
||||
await this.#socket.closed;
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
this.#exit = socket.closed.then(() => 0);
|
||||
|
|
|
@ -15,23 +15,25 @@ export interface AdbSyncEntry extends AdbSyncStat {
|
|||
name: string;
|
||||
}
|
||||
|
||||
export const AdbSyncEntryResponse = struct(
|
||||
/* #__PURE__ */ (() => ({
|
||||
export const AdbSyncEntryResponse = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
...AdbSyncLstatResponse.fields,
|
||||
name: string(u32),
|
||||
}))(),
|
||||
},
|
||||
{ littleEndian: true, extra: AdbSyncLstatResponse.extra },
|
||||
);
|
||||
))();
|
||||
|
||||
export type AdbSyncEntryResponse = StructValue<typeof AdbSyncEntryResponse>;
|
||||
|
||||
export const AdbSyncEntry2Response = struct(
|
||||
/* #__PURE__ */ (() => ({
|
||||
export const AdbSyncEntry2Response = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
...AdbSyncStatResponse.fields,
|
||||
name: string(u32),
|
||||
}))(),
|
||||
},
|
||||
{ littleEndian: true, extra: AdbSyncStatResponse.extra },
|
||||
);
|
||||
))();
|
||||
|
||||
export type AdbSyncEntry2Response = StructValue<typeof AdbSyncEntry2Response>;
|
||||
|
||||
|
|
|
@ -43,11 +43,13 @@ export interface AdbCredentialStore {
|
|||
iterateKeys(): AdbKeyIterable;
|
||||
}
|
||||
|
||||
export enum AdbAuthType {
|
||||
Token = 1,
|
||||
Signature = 2,
|
||||
PublicKey = 3,
|
||||
}
|
||||
export const AdbAuthType = {
|
||||
Token: 1,
|
||||
Signature: 2,
|
||||
PublicKey: 3,
|
||||
} as const;
|
||||
|
||||
export type AdbAuthType = (typeof AdbAuthType)[keyof typeof AdbAuthType];
|
||||
|
||||
export interface AdbAuthenticator {
|
||||
/**
|
||||
|
|
|
@ -28,7 +28,8 @@ import { AdbCommand, calculateChecksum } from "./packet.js";
|
|||
export const ADB_DAEMON_VERSION_OMIT_CHECKSUM = 0x01000001;
|
||||
// https://android.googlesource.com/platform/packages/modules/adb/+/79010dc6d5ca7490c493df800d4421730f5466ca/transport.cpp#1252
|
||||
// There are some other feature constants, but some of them are only used by ADB server, not devices (daemons).
|
||||
export const ADB_DAEMON_DEFAULT_FEATURES = [
|
||||
export const ADB_DAEMON_DEFAULT_FEATURES = /* #__PURE__ */ (() =>
|
||||
[
|
||||
AdbFeature.ShellV2,
|
||||
AdbFeature.Cmd,
|
||||
AdbFeature.StatV2,
|
||||
|
@ -48,7 +49,7 @@ export const ADB_DAEMON_DEFAULT_FEATURES = [
|
|||
"sendrecv_v2_zstd",
|
||||
"sendrecv_v2_dry_run_send",
|
||||
AdbFeature.DelayedAck,
|
||||
] as AdbFeature[];
|
||||
] as AdbFeature[])();
|
||||
export const ADB_DAEMON_DEFAULT_INITIAL_PAYLOAD_SIZE = 32 * 1024 * 1024;
|
||||
|
||||
export type AdbDaemonConnection = ReadableWritablePair<
|
||||
|
|
|
@ -211,12 +211,10 @@ export class BugReport extends AdbCommandBase {
|
|||
let filename: string | undefined;
|
||||
let error: string | undefined;
|
||||
|
||||
await process.stdout
|
||||
for await (const line of process.stdout
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(new SplitStringStream("\n"))
|
||||
.pipeTo(
|
||||
new WritableStream<string>({
|
||||
write(line) {
|
||||
// Each chunk should contain one or several full lines
|
||||
.pipeThrough(new SplitStringStream("\n"))) {
|
||||
// `BEGIN:` and `PROGRESS:` only appear when `-p` is specified.
|
||||
let match = line.match(BugReport.PROGRESS_REGEX);
|
||||
if (match) {
|
||||
|
@ -239,9 +237,7 @@ export class BugReport extends AdbCommandBase {
|
|||
// We want to gather all output.
|
||||
error = match[1];
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
SplitStringStream,
|
||||
TextDecoderStream,
|
||||
WrapReadableStream,
|
||||
WritableStream,
|
||||
} from "@yume-chan/stream-extra";
|
||||
import type { AsyncExactReadable, StructValue } from "@yume-chan/struct";
|
||||
import { decodeUtf8, struct, u16, u32 } from "@yume-chan/struct";
|
||||
|
@ -437,13 +436,10 @@ export class Logcat extends AdbCommandBase {
|
|||
]);
|
||||
|
||||
const result: LogSize[] = [];
|
||||
await stdout
|
||||
for await (const line of stdout
|
||||
.pipeThrough(new TextDecoderStream())
|
||||
.pipeThrough(new SplitStringStream("\n"))
|
||||
.pipeTo(
|
||||
new WritableStream({
|
||||
write(chunk) {
|
||||
let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
|
||||
.pipeThrough(new SplitStringStream("\n"))) {
|
||||
let match = line.match(Logcat.LOG_SIZE_REGEX_11);
|
||||
if (match) {
|
||||
result.push({
|
||||
id: Logcat.logNameToId(match[1]!),
|
||||
|
@ -462,10 +458,10 @@ export class Logcat extends AdbCommandBase {
|
|||
maxEntrySize: parseInt(match[8]!, 10),
|
||||
maxPayloadSize: parseInt(match[9]!, 10),
|
||||
});
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
|
||||
match = line.match(Logcat.LOG_SIZE_REGEX_10);
|
||||
if (match) {
|
||||
result.push({
|
||||
id: Logcat.logNameToId(match[1]!),
|
||||
|
@ -481,9 +477,7 @@ export class Logcat extends AdbCommandBase {
|
|||
maxPayloadSize: parseInt(match[7]!, 10),
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export * from "../../esm/index";
|
|
@ -6,7 +6,7 @@ export const VideoOrientation = {
|
|||
Landscape: 1,
|
||||
PortraitFlipped: 2,
|
||||
LandscapeFlipped: 3,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type VideoOrientation =
|
||||
(typeof VideoOrientation)[keyof typeof VideoOrientation];
|
||||
|
@ -85,6 +85,10 @@ export class CodecOptions implements ScrcpyOptionValue {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace CodecOptions {
|
||||
export type Init = CodecOptionsInit;
|
||||
}
|
||||
|
||||
export interface Init {
|
||||
logLevel?: LogLevel;
|
||||
|
||||
|
|
|
@ -4,18 +4,7 @@ import { bipedal, struct, u16, u32, u64, u8 } from "@yume-chan/struct";
|
|||
|
||||
import type { AndroidMotionEventAction } from "../../android/index.js";
|
||||
import type { ScrcpyInjectTouchControlMessage } from "../../latest.js";
|
||||
|
||||
export function clamp(value: number, min: number, max: number): number {
|
||||
if (value < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (value > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
import { clamp } from "../../utils/index.js";
|
||||
|
||||
export const UnsignedFloat: Field<number, never, never> = {
|
||||
size: 2,
|
||||
|
@ -33,6 +22,13 @@ export const UnsignedFloat: Field<number, never, never> = {
|
|||
}),
|
||||
};
|
||||
|
||||
export const PointerId = {
|
||||
Mouse: -1n,
|
||||
Finger: -2n,
|
||||
VirtualMouse: -3n,
|
||||
VirtualFinger: -4n,
|
||||
} as const;
|
||||
|
||||
export const InjectTouchControlMessage = struct(
|
||||
{
|
||||
type: u8,
|
||||
|
|
|
@ -5,6 +5,7 @@ export {
|
|||
} from "./back-or-screen-on.js";
|
||||
export { ControlMessageTypes } from "./control-message-types.js";
|
||||
export { Defaults } from "./defaults.js";
|
||||
export type { Init } from "./init.js";
|
||||
export { VideoOrientation } from "./init.js";
|
||||
export type { Init, LogLevel } from "./init.js";
|
||||
export { EncoderRegex } from "./parse-encoder.js";
|
||||
export { SerializeOrder } from "./serialize-order.js";
|
||||
|
|
|
@ -9,7 +9,7 @@ export const VideoOrientation = {
|
|||
Landscape: 1,
|
||||
PortraitFlipped: 2,
|
||||
LandscapeFlipped: 3,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type VideoOrientation =
|
||||
(typeof VideoOrientation)[keyof typeof VideoOrientation];
|
||||
|
|
|
@ -4,15 +4,16 @@ function toSnakeCase(input: string): string {
|
|||
return input.replace(/([A-Z])/g, "_$1").toLowerCase();
|
||||
}
|
||||
|
||||
// 1.21 changed the format of arguments
|
||||
export function serialize<T extends object>(
|
||||
options: T,
|
||||
defaults: Required<T>,
|
||||
): string[] {
|
||||
// 1.21 changed the format of arguments
|
||||
const result: string[] = [];
|
||||
for (const [key, value] of Object.entries(options)) {
|
||||
const serializedValue = toScrcpyOptionValue(value, undefined);
|
||||
if (!serializedValue) {
|
||||
// v3.0 `new_display` option needs to send empty strings to server
|
||||
if (serializedValue === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -20,7 +21,7 @@ export function serialize<T extends object>(
|
|||
defaults[key as keyof T],
|
||||
undefined,
|
||||
);
|
||||
if (serializedValue == defaultValue) {
|
||||
if (serializedValue === defaultValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import type { StructInit } from "@yume-chan/struct";
|
||||
import { s32, struct } from "@yume-chan/struct";
|
||||
|
||||
import type { ScrcpyInjectScrollControlMessage } from "../../latest.js";
|
||||
|
||||
import { PrevImpl } from "./prev.js";
|
||||
|
||||
export const InjectScrollControlMessage = /* #__PURE__ */ (() =>
|
||||
|
@ -18,7 +20,7 @@ export type InjectScrollControlMessage = StructInit<
|
|||
|
||||
export class ScrollController extends PrevImpl.ScrollController {
|
||||
override serializeScrollMessage(
|
||||
message: InjectScrollControlMessage,
|
||||
message: ScrcpyInjectScrollControlMessage,
|
||||
): Uint8Array | undefined {
|
||||
const processed = this.processMessage(message);
|
||||
if (!processed) {
|
||||
|
|
|
@ -2,16 +2,16 @@ import { getInt16, setInt16 } from "@yume-chan/no-data-view";
|
|||
import type { Field, StructInit } from "@yume-chan/struct";
|
||||
import { bipedal, struct, u16, u32, u8 } from "@yume-chan/struct";
|
||||
|
||||
import { ScrcpyControlMessageType } from "../../base/index.js";
|
||||
import type { ScrcpyScrollController } from "../../base/index.js";
|
||||
import type { ScrcpyInjectScrollControlMessage } from "../../latest.js";
|
||||
|
||||
import { PrevImpl } from "./prev.js";
|
||||
import { clamp } from "../../utils/index.js";
|
||||
|
||||
export const SignedFloat: Field<number, never, never> = {
|
||||
size: 2,
|
||||
serialize(value, { buffer, index, littleEndian }) {
|
||||
// https://github.com/Genymobile/scrcpy/blob/1f138aef41de651668043b32c4effc2d4adbfc44/app/src/util/binary.h#L51
|
||||
value = PrevImpl.clamp(value, -1, 1);
|
||||
value = clamp(value, -1, 1);
|
||||
value = value === 1 ? 0x7fff : value * 0x8000;
|
||||
setInt16(buffer, index, value, littleEndian);
|
||||
},
|
||||
|
@ -23,9 +23,10 @@ export const SignedFloat: Field<number, never, never> = {
|
|||
}),
|
||||
};
|
||||
|
||||
export const InjectScrollControlMessage = struct(
|
||||
export const InjectScrollControlMessage = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
type: u8,
|
||||
type: u8(ScrcpyControlMessageType.InjectScroll),
|
||||
pointerX: u32,
|
||||
pointerY: u32,
|
||||
screenWidth: u16,
|
||||
|
@ -35,7 +36,7 @@ export const InjectScrollControlMessage = struct(
|
|||
buttons: u32,
|
||||
},
|
||||
{ littleEndian: false },
|
||||
);
|
||||
))();
|
||||
|
||||
export type InjectScrollControlMessage = StructInit<
|
||||
typeof InjectScrollControlMessage
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { omit } from "../../utils/index.js";
|
||||
|
||||
import type { Init } from "./init.js";
|
||||
import { InstanceId } from "./init.js";
|
||||
import { PrevImpl } from "./prev.js";
|
||||
|
||||
export const Defaults = /* #__PURE__ */ (() =>
|
||||
({
|
||||
...PrevImpl.Defaults,
|
||||
...omit(PrevImpl.Defaults, "bitRate", "codecOptions", "encoderName"),
|
||||
scid: InstanceId.NONE,
|
||||
|
||||
videoCodec: "h264",
|
||||
|
|
|
@ -6,6 +6,7 @@ export {
|
|||
serializeInjectTouchControlMessage,
|
||||
} from "./inject-touch.js";
|
||||
export * from "./parse-audio-stream-metadata.js";
|
||||
export { parseDisplay } from "./parse-display.js";
|
||||
export { parseEncoder } from "./parse-encoder.js";
|
||||
export { parseVideoStreamMetadata } from "./parse-video-stream-metadata.js";
|
||||
export { setListDisplays } from "./set-list-display.js";
|
||||
|
|
|
@ -2,13 +2,15 @@ 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 = struct(
|
||||
export const InjectTouchControlMessage = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
type: u8,
|
||||
type: u8(ScrcpyControlMessageType.InjectTouch),
|
||||
action: u8<AndroidMotionEventAction>(),
|
||||
pointerId: u64,
|
||||
pointerX: u32,
|
||||
|
@ -20,7 +22,7 @@ export const InjectTouchControlMessage = struct(
|
|||
buttons: u32,
|
||||
},
|
||||
{ littleEndian: false },
|
||||
);
|
||||
))();
|
||||
|
||||
export type InjectTouchControlMessage = StructInit<
|
||||
typeof InjectTouchControlMessage
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import type { ScrcpyDisplay } from "../../base/index.js";
|
||||
|
||||
export function parseDisplay(line: string): ScrcpyDisplay | undefined {
|
||||
// The client-side option name is `--display`
|
||||
// but the server-side option name is always `display_id`
|
||||
const match = line.match(/^\s+--display=(\d+)\s+\(([^)]+)\)$/);
|
||||
if (match) {
|
||||
const display: ScrcpyDisplay = {
|
||||
|
|
|
@ -41,7 +41,7 @@ async function parseAsync(
|
|||
let width: number | undefined;
|
||||
let height: number | undefined;
|
||||
if (options.sendCodecMeta) {
|
||||
codec = await PrevImpl.readU32(buffered);
|
||||
codec = (await PrevImpl.readU32(buffered)) as ScrcpyVideoCodecId;
|
||||
width = await PrevImpl.readU32(buffered);
|
||||
height = await PrevImpl.readU32(buffered);
|
||||
} else {
|
||||
|
@ -64,9 +64,7 @@ export function parseVideoStreamMetadata(
|
|||
if (!options.sendDeviceMeta && !options.sendCodecMeta) {
|
||||
return {
|
||||
stream,
|
||||
metadata: {
|
||||
codec: toCodecId(options.videoCodec),
|
||||
},
|
||||
metadata: { codec: toCodecId(options.videoCodec) },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export * from "../../2_0/impl/index.js";
|
||||
export * from "../../2_1/impl/index.js";
|
||||
export { Defaults } from "./defaults.js";
|
||||
export type { Init } from "./init.js";
|
||||
export { parseDisplay } from "./parse-display.js";
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import type { PrevImpl } from "./prev.js";
|
||||
|
||||
export interface Init extends Omit<PrevImpl.Init, "display"> {
|
||||
export interface Init extends PrevImpl.Init {
|
||||
videoSource?: "display" | "camera";
|
||||
displayId?: number;
|
||||
cameraId?: string | undefined;
|
||||
cameraSize?: string | undefined;
|
||||
cameraFacing?: "front" | "back" | "external" | undefined;
|
||||
|
|
|
@ -1 +1 @@
|
|||
export * as PrevImpl from "../../2_0/impl/index.js";
|
||||
export * as PrevImpl from "../../2_1/impl/index.js";
|
||||
|
|
|
@ -54,6 +54,10 @@ export class ScrcpyOptions2_2 implements ScrcpyOptions<Init> {
|
|||
constructor(init: Init) {
|
||||
this.value = { ...Defaults, ...init };
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
this.value.control = false;
|
||||
}
|
||||
|
||||
if (this.value.control && this.value.clipboardAutosync) {
|
||||
this.#clipboard = new ClipboardStream();
|
||||
this.#ackClipboardHandler = new AckClipboardHandler();
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export * from "../../2_0/impl/index.js";
|
||||
export * from "../../2_2/impl/index.js";
|
||||
export type { Init } from "./init.js";
|
||||
|
|
|
@ -1 +1 @@
|
|||
export * as PrevImpl from "../../2_0/impl/index.js";
|
||||
export * as PrevImpl from "../../2_2/impl/index.js";
|
||||
|
|
|
@ -54,6 +54,10 @@ export class ScrcpyOptions2_3 implements ScrcpyOptions<Init> {
|
|||
constructor(init: Init) {
|
||||
this.value = { ...Defaults, ...init };
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
this.value.control = false;
|
||||
}
|
||||
|
||||
if (this.value.control && this.value.clipboardAutosync) {
|
||||
this.#clipboard = new ClipboardStream();
|
||||
this.#ackClipboardHandler = new AckClipboardHandler();
|
||||
|
|
|
@ -17,6 +17,7 @@ import type {
|
|||
ScrcpyInjectTouchControlMessage,
|
||||
ScrcpySetClipboardControlMessage,
|
||||
ScrcpyUHidCreateControlMessage,
|
||||
ScrcpyUHidOutputDeviceMessage,
|
||||
} from "../latest.js";
|
||||
|
||||
import type { Init } from "./impl/index.js";
|
||||
|
@ -55,13 +56,19 @@ export class ScrcpyOptions2_4 implements ScrcpyOptions<Init> {
|
|||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
#uHidOutput: UHidOutputStream | undefined;
|
||||
get uHidOutput(): UHidOutputStream | undefined {
|
||||
get uHidOutput():
|
||||
| ReadableStream<ScrcpyUHidOutputDeviceMessage>
|
||||
| undefined {
|
||||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init) {
|
||||
this.value = { ...Defaults, ...init };
|
||||
|
||||
if (this.value.videoSource === "camera") {
|
||||
this.value.control = false;
|
||||
}
|
||||
|
||||
if (this.value.control) {
|
||||
if (this.value.clipboardAutosync) {
|
||||
this.#clipboard = new ClipboardStream();
|
||||
|
|
|
@ -17,9 +17,9 @@ import type {
|
|||
ScrcpyInjectTouchControlMessage,
|
||||
ScrcpySetClipboardControlMessage,
|
||||
ScrcpyUHidCreateControlMessage,
|
||||
ScrcpyUHidOutputDeviceMessage,
|
||||
} from "../latest.js";
|
||||
|
||||
import type { Init } from "./impl/index.js";
|
||||
import {
|
||||
AckClipboardHandler,
|
||||
ClipboardStream,
|
||||
|
@ -37,7 +37,10 @@ import {
|
|||
serializeUHidCreateControlMessage,
|
||||
setListDisplays,
|
||||
setListEncoders,
|
||||
UHidOutputStream
|
||||
|
||||
} from "./impl/index.js";
|
||||
import type {Init} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_6 implements ScrcpyOptions<Init> {
|
||||
readonly value: Required<Init>;
|
||||
|
@ -53,13 +56,28 @@ export class ScrcpyOptions2_6 implements ScrcpyOptions<Init> {
|
|||
|
||||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
#uHidOutput: UHidOutputStream | undefined;
|
||||
get uHidOutput():
|
||||
| ReadableStream<ScrcpyUHidOutputDeviceMessage>
|
||||
| undefined {
|
||||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init) {
|
||||
this.value = { ...Defaults, ...init };
|
||||
|
||||
if (this.value.control && this.value.clipboardAutosync) {
|
||||
if (this.value.videoSource === "camera") {
|
||||
this.value.control = false;
|
||||
}
|
||||
|
||||
if (this.value.control) {
|
||||
if (this.value.clipboardAutosync) {
|
||||
this.#clipboard = new ClipboardStream();
|
||||
this.#ackClipboardHandler = new AckClipboardHandler();
|
||||
}
|
||||
|
||||
this.#uHidOutput = new UHidOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
serialize(): string[] {
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import type { StructInit } from "@yume-chan/struct";
|
||||
import { buffer, string, struct, u16, u8 } from "@yume-chan/struct";
|
||||
|
||||
import { ScrcpyControlMessageType } from "../../base/control-message-type.js";
|
||||
import type { ScrcpyUHidCreateControlMessage } from "../../latest.js";
|
||||
|
||||
export const UHidCreateControlMessage = struct(
|
||||
export const UHidCreateControlMessage = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
type: u8,
|
||||
type: u8(ScrcpyControlMessageType.UHidCreate),
|
||||
id: u16,
|
||||
name: string(u8),
|
||||
data: buffer(u16),
|
||||
},
|
||||
{ littleEndian: false },
|
||||
);
|
||||
))();
|
||||
|
||||
export type UHidCreateControlMessage = StructInit<
|
||||
typeof UHidCreateControlMessage
|
||||
|
|
|
@ -17,6 +17,7 @@ import type {
|
|||
ScrcpyInjectTouchControlMessage,
|
||||
ScrcpySetClipboardControlMessage,
|
||||
ScrcpyUHidCreateControlMessage,
|
||||
ScrcpyUHidOutputDeviceMessage,
|
||||
} from "../latest.js";
|
||||
|
||||
import type { Init } from "./impl/index.js";
|
||||
|
@ -37,6 +38,7 @@ import {
|
|||
serializeUHidCreateControlMessage,
|
||||
setListDisplays,
|
||||
setListEncoders,
|
||||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptions2_7 implements ScrcpyOptions<Init> {
|
||||
|
@ -53,13 +55,28 @@ export class ScrcpyOptions2_7 implements ScrcpyOptions<Init> {
|
|||
|
||||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
#uHidOutput: UHidOutputStream | undefined;
|
||||
get uHidOutput():
|
||||
| ReadableStream<ScrcpyUHidOutputDeviceMessage>
|
||||
| undefined {
|
||||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init) {
|
||||
this.value = { ...Defaults, ...init };
|
||||
|
||||
if (this.value.control && this.value.clipboardAutosync) {
|
||||
if (this.value.videoSource === "camera") {
|
||||
this.value.control = false;
|
||||
}
|
||||
|
||||
if (this.value.control) {
|
||||
if (this.value.clipboardAutosync) {
|
||||
this.#clipboard = new ClipboardStream();
|
||||
this.#ackClipboardHandler = new AckClipboardHandler();
|
||||
}
|
||||
|
||||
this.#uHidOutput = new UHidOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
serialize(): string[] {
|
||||
|
|
|
@ -2,8 +2,9 @@ import { ScrcpyControlMessageType } from "../../base/index.js";
|
|||
|
||||
import { PrevImpl } from "./prev.js";
|
||||
|
||||
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] = [
|
||||
export const ControlMessageTypes: readonly ScrcpyControlMessageType[] =
|
||||
/* #__PURE__ */ (() => [
|
||||
...PrevImpl.ControlMessageTypes,
|
||||
ScrcpyControlMessageType.StartApp,
|
||||
ScrcpyControlMessageType.ResetVideo,
|
||||
];
|
||||
])();
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
import { omit } from "../../utils/index.js";
|
||||
|
||||
import type { Init } from "./init.js";
|
||||
import { CaptureOrientation, NewDisplay } from "./init.js";
|
||||
import { CaptureOrientation } from "./init.js";
|
||||
import { PrevImpl } from "./prev.js";
|
||||
|
||||
export const Defaults = {
|
||||
...PrevImpl.Defaults,
|
||||
captureOrientation: CaptureOrientation.Default,
|
||||
export const Defaults = /* #__PURE__ */ (() =>
|
||||
({
|
||||
...omit(PrevImpl.Defaults, "lockVideoOrientation"),
|
||||
captureOrientation: CaptureOrientation.Unlocked,
|
||||
angle: 0,
|
||||
screenOffTimeout: undefined,
|
||||
listApps: false,
|
||||
newDisplay: NewDisplay.Empty,
|
||||
newDisplay: undefined,
|
||||
vdSystemDecorations: true,
|
||||
} as const satisfies Required<Init>;
|
||||
}) as const satisfies Required<Init>)();
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
export * from "../../2_7/impl/index.js";
|
||||
export { ControlMessageTypes } from "./control-message-types.js";
|
||||
export { Defaults } from "./defaults.js";
|
||||
export type { Init } from "./init.js";
|
||||
export { EncoderRegex } from "./parse-encoder.js";
|
||||
export {
|
||||
CaptureOrientation,
|
||||
LockOrientation,
|
||||
NewDisplay,
|
||||
Orientation,
|
||||
type Init,
|
||||
} from "./init.js";
|
||||
|
|
|
@ -6,7 +6,7 @@ export const LockOrientation = {
|
|||
Unlocked: 0,
|
||||
LockedInitial: 1,
|
||||
LockedValue: 2,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type LockOrientation =
|
||||
(typeof LockOrientation)[keyof typeof LockOrientation];
|
||||
|
@ -16,16 +16,17 @@ export const Orientation = {
|
|||
Orient90: 90,
|
||||
Orient180: 180,
|
||||
Orient270: 270,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type Orientation = (typeof Orientation)[keyof typeof Orientation];
|
||||
|
||||
export class CaptureOrientation implements ScrcpyOptionValue {
|
||||
static Default = /* #__PURE__ */ new CaptureOrientation(
|
||||
static Unlocked = /* #__PURE__ */ (() =>
|
||||
new CaptureOrientation(
|
||||
LockOrientation.Unlocked,
|
||||
Orientation.Orient0,
|
||||
false,
|
||||
);
|
||||
))();
|
||||
|
||||
lock: LockOrientation;
|
||||
orientation: Orientation;
|
||||
|
@ -59,7 +60,7 @@ export class CaptureOrientation implements ScrcpyOptionValue {
|
|||
}
|
||||
|
||||
export class NewDisplay implements ScrcpyOptionValue {
|
||||
static Empty = /* #__PURE__ */ new NewDisplay();
|
||||
static Default = /* #__PURE__ */ new NewDisplay();
|
||||
|
||||
width?: number | undefined;
|
||||
height?: number | undefined;
|
||||
|
@ -90,7 +91,7 @@ export class NewDisplay implements ScrcpyOptionValue {
|
|||
this.height === undefined &&
|
||||
this.dpi === undefined
|
||||
) {
|
||||
return undefined;
|
||||
return "";
|
||||
}
|
||||
|
||||
if (this.width === undefined) {
|
||||
|
@ -112,6 +113,9 @@ export interface Init extends Omit<PrevImpl.Init, "lockVideoOrientation"> {
|
|||
|
||||
listApps?: boolean;
|
||||
|
||||
newDisplay?: NewDisplay;
|
||||
// `display_id` and `new_display` can't be specified at the same time
|
||||
// but `serialize` method will exclude options that are same as the default value
|
||||
// so `displayId: 0` will be ignored
|
||||
newDisplay?: NewDisplay | undefined;
|
||||
vdSystemDecorations?: boolean;
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export const EncoderRegex = /^\s+scrcpy --encoder-name '([^']+)'$/;
|
|
@ -1,2 +1,2 @@
|
|||
export * as ScrcpyOptionsX_XXImpl from "./impl/index.js";
|
||||
export * as ScrcpyOptions3_0Impl from "./impl/index.js";
|
||||
export * from "./options.js";
|
||||
|
|
|
@ -17,6 +17,7 @@ import type {
|
|||
ScrcpyInjectTouchControlMessage,
|
||||
ScrcpySetClipboardControlMessage,
|
||||
ScrcpyUHidCreateControlMessage,
|
||||
ScrcpyUHidOutputDeviceMessage,
|
||||
} from "../latest.js";
|
||||
|
||||
import type { Init } from "./impl/index.js";
|
||||
|
@ -37,9 +38,10 @@ import {
|
|||
serializeUHidCreateControlMessage,
|
||||
setListDisplays,
|
||||
setListEncoders,
|
||||
UHidOutputStream,
|
||||
} from "./impl/index.js";
|
||||
|
||||
export class ScrcpyOptionsX_X implements ScrcpyOptions<Init> {
|
||||
export class ScrcpyOptions3_0 implements ScrcpyOptions<Init> {
|
||||
readonly value: Required<Init>;
|
||||
|
||||
get controlMessageTypes(): readonly ScrcpyControlMessageType[] {
|
||||
|
@ -53,13 +55,28 @@ export class ScrcpyOptionsX_X implements ScrcpyOptions<Init> {
|
|||
|
||||
#ackClipboardHandler: AckClipboardHandler | undefined;
|
||||
|
||||
#uHidOutput: UHidOutputStream | undefined;
|
||||
get uHidOutput():
|
||||
| ReadableStream<ScrcpyUHidOutputDeviceMessage>
|
||||
| undefined {
|
||||
return this.#uHidOutput;
|
||||
}
|
||||
|
||||
constructor(init: Init) {
|
||||
this.value = { ...Defaults, ...init };
|
||||
|
||||
if (this.value.control && this.value.clipboardAutosync) {
|
||||
if (this.value.videoSource === "camera") {
|
||||
this.value.control = false;
|
||||
}
|
||||
|
||||
if (this.value.control) {
|
||||
if (this.value.clipboardAutosync) {
|
||||
this.#clipboard = new ClipboardStream();
|
||||
this.#ackClipboardHandler = new AckClipboardHandler();
|
||||
}
|
||||
|
||||
this.#uHidOutput = new UHidOutputStream();
|
||||
}
|
||||
}
|
||||
|
||||
serialize(): string[] {
|
||||
|
|
|
@ -13,7 +13,7 @@ export const AndroidMotionEventAction = {
|
|||
HoverExit: 10,
|
||||
ButtonPress: 11,
|
||||
ButtonRelease: 12,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type AndroidMotionEventAction =
|
||||
(typeof AndroidMotionEventAction)[keyof typeof AndroidMotionEventAction];
|
||||
|
@ -27,7 +27,7 @@ export const AndroidMotionEventButton = {
|
|||
Forward: 16,
|
||||
StylusPrimary: 32,
|
||||
StylusSecondary: 64,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type AndroidMotionEventButton =
|
||||
(typeof AndroidMotionEventButton)[keyof typeof AndroidMotionEventButton];
|
||||
|
|
|
@ -18,7 +18,7 @@ export const ScrcpyControlMessageType = {
|
|||
OpenHardKeyboardSettings: 15,
|
||||
StartApp: 16,
|
||||
ResetVideo: 17,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type ScrcpyControlMessageType =
|
||||
(typeof ScrcpyControlMessageType)[keyof typeof ScrcpyControlMessageType];
|
||||
|
|
|
@ -7,6 +7,7 @@ import type {
|
|||
ScrcpyInjectTouchControlMessage,
|
||||
ScrcpySetClipboardControlMessage,
|
||||
ScrcpyUHidCreateControlMessage,
|
||||
ScrcpyUHidOutputDeviceMessage,
|
||||
} from "../latest.js";
|
||||
|
||||
import type { ScrcpyAudioStreamMetadata } from "./audio.js";
|
||||
|
@ -22,7 +23,11 @@ export interface ScrcpyOptions<T extends object> {
|
|||
|
||||
value: Required<T>;
|
||||
|
||||
get clipboard(): ReadableStream<string> | undefined;
|
||||
readonly clipboard?: ReadableStream<string> | undefined;
|
||||
|
||||
readonly uHidOutput?:
|
||||
| ReadableStream<ScrcpyUHidOutputDeviceMessage>
|
||||
| undefined;
|
||||
|
||||
serialize(): string[];
|
||||
|
||||
|
|
|
@ -16,7 +16,17 @@ export const ScrcpyVideoCodecId = {
|
|||
H264: 0x68_32_36_34,
|
||||
H265: 0x68_32_36_35,
|
||||
AV1: 0x00_61_76_31,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export type ScrcpyVideoCodecId =
|
||||
(typeof ScrcpyVideoCodecId)[keyof typeof ScrcpyVideoCodecId];
|
||||
|
||||
export const ScrcpyVideoCodecNameMap = /* #__PURE__ */ (() => {
|
||||
const result = new Map<number, string>();
|
||||
for (const key in ScrcpyVideoCodecId) {
|
||||
const value =
|
||||
ScrcpyVideoCodecId[key as keyof typeof ScrcpyVideoCodecId];
|
||||
result.set(value, key);
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
|
|
@ -136,7 +136,7 @@ const ObuType = {
|
|||
RedundantFrameHeader: 7,
|
||||
TileList: 8,
|
||||
Padding: 15,
|
||||
};
|
||||
} as const;
|
||||
|
||||
type ObuType = (typeof ObuType)[keyof typeof ObuType];
|
||||
|
||||
|
@ -153,7 +153,7 @@ const ColorPrimaries = {
|
|||
Smpte431: 11,
|
||||
Smpte432: 12,
|
||||
Ebu3213: 22,
|
||||
};
|
||||
} as const;
|
||||
|
||||
const TransferCharacteristics = {
|
||||
Bt709: 1,
|
||||
|
@ -173,7 +173,7 @@ const TransferCharacteristics = {
|
|||
Smpte2084: 16,
|
||||
Smpte428: 17,
|
||||
Hlg: 18,
|
||||
};
|
||||
} as const;
|
||||
|
||||
const MatrixCoefficients = {
|
||||
Identity: 0,
|
||||
|
@ -190,7 +190,7 @@ const MatrixCoefficients = {
|
|||
ChromatNcl: 12,
|
||||
ChromatCl: 13,
|
||||
ICtCp: 14,
|
||||
};
|
||||
} as const;
|
||||
|
||||
export class Av1 extends BitReader {
|
||||
static ObuType = ObuType;
|
||||
|
@ -623,13 +623,16 @@ export class Av1 extends BitReader {
|
|||
// const NumPlanes = mono_chrome ? 1 : 3;
|
||||
|
||||
const color_description_present_flag = !!this.f1();
|
||||
let color_primaries = Av1.ColorPrimaries.Unspecified;
|
||||
let transfer_characteristics = Av1.TransferCharacteristics.Unspecified;
|
||||
let matrix_coefficients = Av1.MatrixCoefficients.Unspecified;
|
||||
let color_primaries: Av1.ColorPrimaries =
|
||||
Av1.ColorPrimaries.Unspecified;
|
||||
let transfer_characteristics: Av1.TransferCharacteristics =
|
||||
Av1.TransferCharacteristics.Unspecified;
|
||||
let matrix_coefficients: Av1.MatrixCoefficients =
|
||||
Av1.MatrixCoefficients.Unspecified;
|
||||
if (color_description_present_flag) {
|
||||
color_primaries = this.f(8);
|
||||
transfer_characteristics = this.f(8);
|
||||
matrix_coefficients = this.f(8);
|
||||
color_primaries = this.f(8) as Av1.ColorPrimaries;
|
||||
transfer_characteristics = this.f(8) as Av1.TransferCharacteristics;
|
||||
matrix_coefficients = this.f(8) as Av1.MatrixCoefficients;
|
||||
}
|
||||
|
||||
let color_range = false;
|
||||
|
|
|
@ -6,17 +6,19 @@ import type {
|
|||
AndroidKeyEventAction,
|
||||
AndroidKeyEventMeta,
|
||||
} from "../android/index.js";
|
||||
import { ScrcpyControlMessageType } from "../base/index.js";
|
||||
|
||||
export const ScrcpyInjectKeyCodeControlMessage = struct(
|
||||
export const ScrcpyInjectKeyCodeControlMessage = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
type: u8,
|
||||
type: u8(ScrcpyControlMessageType.InjectKeyCode),
|
||||
action: u8<AndroidKeyEventAction>(),
|
||||
keyCode: u32<AndroidKeyCode>(),
|
||||
repeat: u32,
|
||||
metaState: u32<AndroidKeyEventMeta>(),
|
||||
},
|
||||
{ littleEndian: false },
|
||||
);
|
||||
))();
|
||||
|
||||
export type ScrcpyInjectKeyCodeControlMessage = StructInit<
|
||||
typeof ScrcpyInjectKeyCodeControlMessage
|
||||
|
|
|
@ -24,7 +24,7 @@ export class ScrcpyControlMessageTypeMap {
|
|||
message: Omit<T, "type">,
|
||||
type: T["type"],
|
||||
): T {
|
||||
(message as T).type = this.get(type);
|
||||
(message as T).type = this.get(type) as ScrcpyControlMessageType;
|
||||
return message as T;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import type { StructInit } from "@yume-chan/struct";
|
||||
import { buffer, struct, u16, u8 } from "@yume-chan/struct";
|
||||
|
||||
export const ScrcpyUHidInputControlMessage = struct(
|
||||
import { ScrcpyControlMessageType } from "../base/index.js";
|
||||
|
||||
export const ScrcpyUHidInputControlMessage = /* #__PURE__ */ (() =>
|
||||
struct(
|
||||
{
|
||||
type: u8,
|
||||
type: u8(ScrcpyControlMessageType.UHidInput),
|
||||
id: u16,
|
||||
data: buffer(u16),
|
||||
},
|
||||
{ littleEndian: false },
|
||||
);
|
||||
))();
|
||||
|
||||
export type ScrcpyUHidInputControlMessage = StructInit<
|
||||
typeof ScrcpyUHidInputControlMessage
|
||||
|
|
|
@ -10,10 +10,12 @@ import type {
|
|||
ScrcpyInjectScrollControlMessage,
|
||||
ScrcpyInjectTouchControlMessage,
|
||||
ScrcpySetClipboardControlMessage,
|
||||
ScrcpyUHidCreateControlMessage,
|
||||
} from "../latest.js";
|
||||
|
||||
import type { ScrcpyInjectKeyCodeControlMessage } from "./inject-key-code.js";
|
||||
import { ScrcpyControlMessageSerializer } from "./serializer.js";
|
||||
import type { ScrcpyUHidInputControlMessage } from "./uhid.js";
|
||||
|
||||
export class ScrcpyControlMessageWriter {
|
||||
#writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>;
|
||||
|
@ -27,25 +29,23 @@ export class ScrcpyControlMessageWriter {
|
|||
this.#serializer = new ScrcpyControlMessageSerializer(options);
|
||||
}
|
||||
|
||||
async write(message: Uint8Array) {
|
||||
await Consumable.WritableStream.write(this.#writer, message);
|
||||
write(message: Uint8Array) {
|
||||
return Consumable.WritableStream.write(this.#writer, message);
|
||||
}
|
||||
|
||||
async injectKeyCode(
|
||||
message: Omit<ScrcpyInjectKeyCodeControlMessage, "type">,
|
||||
) {
|
||||
await this.write(this.#serializer.injectKeyCode(message));
|
||||
injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, "type">) {
|
||||
return this.write(this.#serializer.injectKeyCode(message));
|
||||
}
|
||||
|
||||
async injectText(text: string) {
|
||||
await this.write(this.#serializer.injectText(text));
|
||||
injectText(text: string) {
|
||||
return this.write(this.#serializer.injectText(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* `pressure` is a float value between 0 and 1.
|
||||
*/
|
||||
async injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, "type">) {
|
||||
await this.write(this.#serializer.injectTouch(message));
|
||||
injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, "type">) {
|
||||
return this.write(this.#serializer.injectTouch(message));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,24 +67,24 @@ export class ScrcpyControlMessageWriter {
|
|||
}
|
||||
}
|
||||
|
||||
async setScreenPowerMode(mode: AndroidScreenPowerMode) {
|
||||
await this.write(this.#serializer.setDisplayPower(mode));
|
||||
setScreenPowerMode(mode: AndroidScreenPowerMode) {
|
||||
return this.write(this.#serializer.setDisplayPower(mode));
|
||||
}
|
||||
|
||||
async expandNotificationPanel() {
|
||||
await this.write(this.#serializer.expandNotificationPanel());
|
||||
expandNotificationPanel() {
|
||||
return this.write(this.#serializer.expandNotificationPanel());
|
||||
}
|
||||
|
||||
async expandSettingPanel() {
|
||||
await this.write(this.#serializer.expandSettingPanel());
|
||||
expandSettingPanel() {
|
||||
return this.write(this.#serializer.expandSettingPanel());
|
||||
}
|
||||
|
||||
async collapseNotificationPanel() {
|
||||
await this.write(this.#serializer.collapseNotificationPanel());
|
||||
collapseNotificationPanel() {
|
||||
return this.write(this.#serializer.collapseNotificationPanel());
|
||||
}
|
||||
|
||||
async rotateDevice() {
|
||||
await this.write(this.#serializer.rotateDevice());
|
||||
rotateDevice() {
|
||||
return this.write(this.#serializer.rotateDevice());
|
||||
}
|
||||
|
||||
async setClipboard(
|
||||
|
@ -99,6 +99,29 @@ export class ScrcpyControlMessageWriter {
|
|||
}
|
||||
}
|
||||
|
||||
uHidCreate(message: Omit<ScrcpyUHidCreateControlMessage, "type">) {
|
||||
return this.write(this.#serializer.uHidCreate(message));
|
||||
}
|
||||
|
||||
uHidInput(message: Omit<ScrcpyUHidInputControlMessage, "type">) {
|
||||
return this.write(this.#serializer.uHidInput(message));
|
||||
}
|
||||
|
||||
uHidDestroy(id: number) {
|
||||
return this.write(this.#serializer.uHidDestroy(id));
|
||||
}
|
||||
|
||||
startApp(
|
||||
name: string,
|
||||
options?: { forceStop?: boolean; searchByName?: boolean },
|
||||
) {
|
||||
return this.write(this.#serializer.startApp(name, options));
|
||||
}
|
||||
|
||||
resetVideo() {
|
||||
return this.write(this.#serializer.resetVideo());
|
||||
}
|
||||
|
||||
releaseLock() {
|
||||
this.#writer.releaseLock();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ export * from "./2_4/index.js";
|
|||
export * from "./2_5.js";
|
||||
export * from "./2_6/index.js";
|
||||
export * from "./2_7/index.js";
|
||||
export * from "./3_0/index.js";
|
||||
export * from "./android/index.js";
|
||||
export * from "./base/index.js";
|
||||
export * from "./codec/index.js";
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
export {
|
||||
BackOrScreenOnControlMessage as ScrcpyBackOrScreenOnControlMessage,
|
||||
CaptureOrientation as ScrcpyCaptureOrientation,
|
||||
CodecOptions as ScrcpyCodecOptions,
|
||||
InjectScrollControlMessage as ScrcpyInjectScrollControlMessage,
|
||||
InjectTouchControlMessage as ScrcpyInjectTouchControlMessage,
|
||||
InstanceId as ScrcpyInstanceId,
|
||||
LockOrientation as ScrcpyLockOrientation,
|
||||
NewDisplay as ScrcpyNewDisplay,
|
||||
Orientation as ScrcpyOrientation,
|
||||
PointerId as ScrcpyPointerId,
|
||||
SetClipboardControlMessage as ScrcpySetClipboardControlMessage,
|
||||
UHidCreateControlMessage as ScrcpyUHidCreateControlMessage,
|
||||
} from "./2_7/impl/index.js";
|
||||
UHidOutputDeviceMessage as ScrcpyUHidOutputDeviceMessage,
|
||||
} from "./3_0/impl/index.js";
|
||||
|
|
11
libraries/scrcpy/src/utils/clamp.ts
Normal file
11
libraries/scrcpy/src/utils/clamp.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export function clamp(value: number, min: number, max: number): number {
|
||||
if (value < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (value > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
|
@ -1,2 +1,4 @@
|
|||
export * from "./clamp.js";
|
||||
export * from "./constants.js";
|
||||
export * from "./omit.js";
|
||||
export * from "./wrapper.js";
|
||||
|
|
11
libraries/scrcpy/src/utils/omit.ts
Normal file
11
libraries/scrcpy/src/utils/omit.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/* #__NO_SIDE_EFFECTS__ */
|
||||
export function omit<T extends Record<string, unknown>, K extends (keyof T)[]>(
|
||||
value: T,
|
||||
...keys: K
|
||||
): T {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).filter(
|
||||
([key]) => !keys.includes(key as K[number]),
|
||||
),
|
||||
) as never;
|
||||
}
|
|
@ -32,6 +32,10 @@ export class ScrcpyOptionsWrapper<T extends object>
|
|||
return this.#base.clipboard;
|
||||
}
|
||||
|
||||
get uHidOutput() {
|
||||
return this.#base.uHidOutput;
|
||||
}
|
||||
|
||||
constructor(options: ScrcpyOptions<T>) {
|
||||
this.#base = options;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import type {
|
||||
AbortSignal,
|
||||
ReadableStreamIteratorOptions,
|
||||
ReadableStream as ReadableStreamType,
|
||||
TransformStream as TransformStreamType,
|
||||
WritableStream as WritableStreamType,
|
||||
} from "./types.js";
|
||||
|
||||
export * from "./types.js";
|
||||
export { ReadableStream };
|
||||
|
||||
/** A controller object that allows you to abort one or more DOM requests as and when desired. */
|
||||
export interface AbortController {
|
||||
|
@ -32,13 +34,70 @@ interface GlobalExtension {
|
|||
TransformStream: typeof TransformStreamType;
|
||||
}
|
||||
|
||||
export const { AbortController } = globalThis as unknown as GlobalExtension;
|
||||
|
||||
export type ReadableStream<T> = ReadableStreamType<T>;
|
||||
export type WritableStream<T> = WritableStreamType<T>;
|
||||
export type TransformStream<I, O> = TransformStreamType<I, O>;
|
||||
|
||||
export const {
|
||||
AbortController,
|
||||
ReadableStream,
|
||||
WritableStream,
|
||||
TransformStream,
|
||||
} = globalThis as unknown as GlobalExtension;
|
||||
const ReadableStream = /* #__PURE__ */ (() => {
|
||||
const { ReadableStream } = globalThis as unknown as GlobalExtension;
|
||||
|
||||
if (!ReadableStream.from) {
|
||||
ReadableStream.from = function (iterable) {
|
||||
const iterator =
|
||||
Symbol.asyncIterator in iterable
|
||||
? iterable[Symbol.asyncIterator]()
|
||||
: iterable[Symbol.iterator]();
|
||||
|
||||
return new ReadableStream({
|
||||
async pull(controller) {
|
||||
const result = await iterator.next();
|
||||
if (result.done) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
controller.enqueue(result.value);
|
||||
},
|
||||
async cancel(reason) {
|
||||
await iterator.return?.(reason);
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
!ReadableStream.prototype[Symbol.asyncIterator] ||
|
||||
!ReadableStream.prototype.values
|
||||
) {
|
||||
ReadableStream.prototype.values = async function* <R>(
|
||||
this: ReadableStream<R>,
|
||||
options?: ReadableStreamIteratorOptions,
|
||||
) {
|
||||
const reader = this.getReader();
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
yield value;
|
||||
}
|
||||
} finally {
|
||||
if (!options?.preventCancel) {
|
||||
await reader.cancel();
|
||||
}
|
||||
reader.releaseLock();
|
||||
}
|
||||
};
|
||||
|
||||
ReadableStream.prototype[Symbol.asyncIterator] =
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
ReadableStream.prototype.values;
|
||||
}
|
||||
|
||||
return ReadableStream;
|
||||
})();
|
||||
|
||||
export const { WritableStream, TransformStream } =
|
||||
globalThis as unknown as GlobalExtension;
|
||||
|
|
|
@ -242,7 +242,7 @@ export declare class ReadableStream<out R> implements AsyncIterable<R> {
|
|||
* such as an array, an async generator, or a Node.js readable stream.
|
||||
*/
|
||||
static from<R>(
|
||||
asyncIterable: Iterable<R> | AsyncIterable<R> | ReadableStreamLike<R>,
|
||||
asyncIterable: Iterable<R> | AsyncIterable<R>,
|
||||
): ReadableStream<R>;
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ export declare class ReadableStream<out R> implements AsyncIterable<R> {
|
|||
*/
|
||||
export declare interface ReadableStreamAsyncIterator<R>
|
||||
extends AsyncIterableIterator<R> {
|
||||
next(): Promise<IteratorResult<R, undefined>>;
|
||||
next(): Promise<IteratorResult<R, void>>;
|
||||
return(value?: R): Promise<IteratorResult<R>>;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ export interface BufferLike {
|
|||
|
||||
export const EmptyUint8Array = new Uint8Array(0);
|
||||
|
||||
// Rollup doesn't support `/* #__NO_SIDE_EFFECTS__ */ export const a = () => {}
|
||||
/* #__NO_SIDE_EFFECTS__ */
|
||||
function _buffer(
|
||||
export const buffer: BufferLike = function (
|
||||
lengthOrField:
|
||||
| string
|
||||
| number
|
||||
|
@ -259,6 +257,4 @@ function _buffer(
|
|||
return reader.readExactly(length);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const buffer: BufferLike = _buffer as never;
|
||||
} as never;
|
||||
|
|
|
@ -18,11 +18,11 @@ import { bipedal } from "./bipedal.js";
|
|||
import type { Field } from "./field.js";
|
||||
|
||||
export interface NumberField<T> extends Field<T, never, never> {
|
||||
<U>(infer?: T): Field<U, never, never>;
|
||||
<const U>(infer?: U): Field<U, never, never>;
|
||||
}
|
||||
|
||||
/* #__NO_SIDE_EFFECTS__ */
|
||||
function number<T>(
|
||||
function factory<T>(
|
||||
size: number,
|
||||
serialize: Field<T, never, never>["serialize"],
|
||||
deserialize: Field<T, never, never>["deserialize"],
|
||||
|
@ -34,7 +34,7 @@ function number<T>(
|
|||
return result as never;
|
||||
}
|
||||
|
||||
export const u8 = number<number>(
|
||||
export const u8 = factory<number>(
|
||||
1,
|
||||
(value, { buffer, index }) => {
|
||||
buffer[index] = value;
|
||||
|
@ -45,7 +45,7 @@ export const u8 = number<number>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const s8 = number<number>(
|
||||
export const s8 = factory<number>(
|
||||
1,
|
||||
(value, { buffer, index }) => {
|
||||
buffer[index] = value;
|
||||
|
@ -56,7 +56,7 @@ export const s8 = number<number>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const u16 = number<number>(
|
||||
export const u16 = factory<number>(
|
||||
2,
|
||||
(value, { buffer, index, littleEndian }) => {
|
||||
setUint16(buffer, index, value, littleEndian);
|
||||
|
@ -67,7 +67,7 @@ export const u16 = number<number>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const s16 = number<number>(
|
||||
export const s16 = factory<number>(
|
||||
2,
|
||||
(value, { buffer, index, littleEndian }) => {
|
||||
setInt16(buffer, index, value, littleEndian);
|
||||
|
@ -78,7 +78,7 @@ export const s16 = number<number>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const u32 = number<number>(
|
||||
export const u32 = factory<number>(
|
||||
4,
|
||||
(value, { buffer, index, littleEndian }) => {
|
||||
setUint32(buffer, index, value, littleEndian);
|
||||
|
@ -89,7 +89,7 @@ export const u32 = number<number>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const s32 = number<number>(
|
||||
export const s32 = factory<number>(
|
||||
4,
|
||||
(value, { buffer, index, littleEndian }) => {
|
||||
setInt32(buffer, index, value, littleEndian);
|
||||
|
@ -100,7 +100,7 @@ export const s32 = number<number>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const u64 = number<bigint>(
|
||||
export const u64 = factory<bigint>(
|
||||
8,
|
||||
(value, { buffer, index, littleEndian }) => {
|
||||
setUint64(buffer, index, value, littleEndian);
|
||||
|
@ -111,7 +111,7 @@ export const u64 = number<bigint>(
|
|||
}),
|
||||
);
|
||||
|
||||
export const s64 = number<bigint>(
|
||||
export const s64 = factory<bigint>(
|
||||
8,
|
||||
(value, { buffer, index, littleEndian }) => {
|
||||
setInt64(buffer, index, value, littleEndian);
|
||||
|
|
|
@ -33,6 +33,7 @@ export function encodeUtf8(input: string): Uint8Array {
|
|||
return SharedEncoder.encode(input);
|
||||
}
|
||||
|
||||
/* #__NO_SIDE_EFFECTS__ */
|
||||
export function decodeUtf8(buffer: ArrayBufferView | ArrayBuffer): string {
|
||||
// `TextDecoder` has internal states in stream mode,
|
||||
// but this method is not for stream mode, so the instance can be reused
|
||||
|
|
40
pnpm-lock.yaml
generated
40
pnpm-lock.yaml
generated
|
@ -439,21 +439,6 @@ importers:
|
|||
specifier: ^5.6.3
|
||||
version: 5.6.3
|
||||
|
||||
libraries/scrcpy/side-effect-test:
|
||||
devDependencies:
|
||||
'@rollup/plugin-node-resolve':
|
||||
specifier: ^15.3.0
|
||||
version: 15.3.0(rollup@4.27.4)
|
||||
'@rollup/plugin-terser':
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4(rollup@4.27.4)
|
||||
'@rollup/plugin-typescript':
|
||||
specifier: ^12.1.1
|
||||
version: 12.1.1(rollup@4.27.4)(tslib@2.8.1)(typescript@5.6.3)
|
||||
rollup:
|
||||
specifier: ^4.27.4
|
||||
version: 4.27.4
|
||||
|
||||
libraries/stream-extra:
|
||||
dependencies:
|
||||
'@yume-chan/async':
|
||||
|
@ -544,6 +529,31 @@ importers:
|
|||
specifier: ^2.2.3
|
||||
version: 2.2.3
|
||||
|
||||
toolchain/side-effect-test:
|
||||
dependencies:
|
||||
'@yume-chan/adb':
|
||||
specifier: workspace:^
|
||||
version: link:../../libraries/adb
|
||||
'@yume-chan/struct':
|
||||
specifier: workspace:^
|
||||
version: link:../../libraries/struct
|
||||
devDependencies:
|
||||
'@rollup/plugin-node-resolve':
|
||||
specifier: ^15.3.0
|
||||
version: 15.3.0(rollup@4.27.4)
|
||||
'@rollup/plugin-terser':
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4(rollup@4.27.4)
|
||||
'@rollup/plugin-typescript':
|
||||
specifier: ^12.1.1
|
||||
version: 12.1.1(rollup@4.27.4)(tslib@2.8.1)(typescript@5.6.3)
|
||||
rollup:
|
||||
specifier: ^4.27.4
|
||||
version: 4.27.4
|
||||
tslib:
|
||||
specifier: ^2.8.1
|
||||
version: 2.8.1
|
||||
|
||||
toolchain/test-runner:
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"rollup": "^4.27.4"
|
||||
"rollup": "^4.27.4",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@yume-chan/adb": "workspace:^",
|
||||
"@yume-chan/struct": "workspace:^"
|
||||
}
|
||||
}
|
33
toolchain/side-effect-test/src/index.js
Normal file
33
toolchain/side-effect-test/src/index.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
bipedal,
|
||||
buffer,
|
||||
decodeUtf8,
|
||||
encodeUtf8,
|
||||
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({}, {});
|
||||
|
||||
export * from "@yume-chan/struct";
|
Loading…
Add table
Add a link
Reference in a new issue