Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c13bbe3a authored by Simon Chan's avatar Simon Chan
Browse files

refactor(scrcpy): revert changing control message client to stream

refs #427
parent 32f98424
Loading
Loading
Loading
Loading
+18 −33
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ import { CSSProperties, ReactNode, useEffect, useState } from "react";

import { ADB_SYNC_MAX_PACKET_SIZE } from '@yume-chan/adb';
import { EventEmitter } from "@yume-chan/event";
import { AdbScrcpyClient, AdbScrcpyOptions1_22, AndroidKeyCode, AndroidKeyEventAction, AndroidMotionEventAction, CodecOptions, DEFAULT_SERVER_PATH, ScrcpyControlMessage, ScrcpyControlMessageType, ScrcpyLogLevel, ScrcpyOptions1_24, ScrcpyVideoOrientation, TinyH264Decoder, WebCodecsDecoder, type H264Decoder, type H264DecoderConstructor, type VideoStreamPacket } from "@yume-chan/scrcpy";
import { AdbScrcpyClient, AdbScrcpyOptions1_22, AndroidKeyCode, AndroidKeyEventAction, AndroidMotionEventAction, CodecOptions, DEFAULT_SERVER_PATH, ScrcpyDeviceMessageType, ScrcpyLogLevel, ScrcpyOptions1_24, ScrcpyVideoOrientation, TinyH264Decoder, WebCodecsDecoder, type H264Decoder, type H264DecoderConstructor, type VideoStreamPacket } from "@yume-chan/scrcpy";
import SCRCPY_SERVER_VERSION from '@yume-chan/scrcpy/bin/version';
import { ChunkStream, InspectStream, ReadableStream, WritableStream } from '@yume-chan/stream-extra';

@@ -243,7 +243,6 @@ class ScrcpyPageState {
    get rotatedHeight() { return state.rotate & 1 ? state.width : state.height; }

    client: AdbScrcpyClient | undefined = undefined;
    controlMessageWriter: WritableStreamDefaultWriter<ScrcpyControlMessage> | undefined = undefined;

    async pushServer() {
        const serverBuffer = await fetchServer();
@@ -364,7 +363,7 @@ class ScrcpyPageState {
            iconProps: { iconName: Icons.Orientation },
            iconOnly: true,
            text: 'Rotate Device',
            onClick: () => { this.controlMessageWriter!.write({ type: ScrcpyControlMessageType.RotateDevice }); },
            onClick: () => { this.client!.controlMessageSerializer!.rotateDevice(); },
        });

        result.push({
@@ -795,13 +794,16 @@ class ScrcpyPageState {

            client.deviceMessageStream!.pipeTo(new WritableStream({
                write(message) {
                    switch (message.type) {
                        case ScrcpyDeviceMessageType.Clipboard:
                            window.navigator.clipboard.writeText(message.content);
                            break;
                    }
                }
            })).catch(() => { });

            runInAction(() => {
                this.client = client;
                this.controlMessageWriter = client.controlMessageStream!.getWriter();
                this.running = true;
            });
        } catch (e: any) {
@@ -850,10 +852,7 @@ class ScrcpyPageState {
        }
        e.currentTarget.setPointerCapture(e.pointerId);

        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.BackOrScreenOn,
            action: AndroidKeyEventAction.Down,
        });
        this.client!.controlMessageSerializer!.backOrScreenOn(AndroidKeyEventAction.Down);
    };

    handleBackPointerUp = (e: React.PointerEvent<HTMLDivElement>) => {
@@ -865,10 +864,7 @@ class ScrcpyPageState {
            return;
        }

        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.BackOrScreenOn,
            action: AndroidKeyEventAction.Up,
        });
        this.client!.controlMessageSerializer!.backOrScreenOn(AndroidKeyEventAction.Up);
    };

    handleHomePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
@@ -881,8 +877,7 @@ class ScrcpyPageState {
        }
        e.currentTarget.setPointerCapture(e.pointerId);

        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.InjectKeyCode,
        this.client!.controlMessageSerializer!.injectKeyCode({
            action: AndroidKeyEventAction.Down,
            keyCode: AndroidKeyCode.Home,
            repeat: 0,
@@ -899,8 +894,7 @@ class ScrcpyPageState {
            return;
        }

        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.InjectKeyCode,
        this.client!.controlMessageSerializer!.injectKeyCode({
            action: AndroidKeyEventAction.Up,
            keyCode: AndroidKeyCode.Home,
            repeat: 0,
@@ -918,8 +912,7 @@ class ScrcpyPageState {
        }
        e.currentTarget.setPointerCapture(e.pointerId);

        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.InjectKeyCode,
        this.client!.controlMessageSerializer!.injectKeyCode({
            action: AndroidKeyEventAction.Down,
            keyCode: AndroidKeyCode.AppSwitch,
            repeat: 0,
@@ -936,8 +929,7 @@ class ScrcpyPageState {
            return;
        }

        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.InjectKeyCode,
        this.client!.controlMessageSerializer!.injectKeyCode({
            action: AndroidKeyEventAction.Up,
            keyCode: AndroidKeyCode.AppSwitch,
            repeat: 0,
@@ -981,8 +973,7 @@ class ScrcpyPageState {
        }

        const { x, y } = this.calculatePointerPosition(e.clientX, e.clientY);
        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.InjectTouch,
        this.client!.controlMessageSerializer!.injectTouch({
            action,
            pointerId: e.pointerType === "mouse" ? BigInt(-1) : BigInt(e.pointerId),
            screenWidth: this.client!.screenWidth!,
@@ -1021,8 +1012,7 @@ class ScrcpyPageState {
        e.stopPropagation();

        const { x, y } = this.calculatePointerPosition(e.clientX, e.clientY);
        this.controlMessageWriter!.write({
            type: ScrcpyControlMessageType.InjectScroll,
        this.client!.controlMessageSerializer!.injectScroll({
            screenWidth: this.client!.screenWidth!,
            screenHeight: this.client!.screenHeight!,
            pointerX: x,
@@ -1044,10 +1034,7 @@ class ScrcpyPageState {

        const { key, code } = e;
        if (key.match(/^[!-`{-~]$/i)) {
            this.controlMessageWriter!.write({
                type: ScrcpyControlMessageType.InjectText,
                text: key,
            });
            this.client!.controlMessageSerializer!.injectText(key);
            return;
        }

@@ -1058,15 +1045,13 @@ class ScrcpyPageState {
        } as Record<string, AndroidKeyCode | undefined>)[code];

        if (keyCode) {
            this.controlMessageWriter!.write({
                type: ScrcpyControlMessageType.InjectKeyCode,
            this.client!.controlMessageSerializer!.injectKeyCode({
                action: AndroidKeyEventAction.Down,
                keyCode,
                metaState: 0,
                repeat: 0,
            });
            this.controlMessageWriter!.write({
                type: ScrcpyControlMessageType.InjectKeyCode,
            this.client!.controlMessageSerializer!.injectKeyCode({
                action: AndroidKeyEventAction.Up,
                keyCode,
                metaState: 0,
+5 −5
Original line number Diff line number Diff line
import { Adb, AdbSubprocessNoneProtocol, AdbSubprocessProtocol, AdbSync } from '@yume-chan/adb';
import { DecodeUtf8Stream, InspectStream, pipeFrom, ReadableStream, SplitStringStream, WrapWritableStream, WritableStream, type ReadableWritablePair } from '@yume-chan/stream-extra';
import { DecodeUtf8Stream, InspectStream, ReadableStream, SplitStringStream, WrapWritableStream, WritableStream, type ReadableWritablePair } from '@yume-chan/stream-extra';

import { ScrcpyControlMessageSerializeStream, type ScrcpyControlMessage } from '../control/index.js';
import { ScrcpyControlMessageSerializer } from '../control/index.js';
import { ScrcpyDeviceMessageDeserializeStream, type ScrcpyDeviceMessage } from '../device-message/index.js';
import { DEFAULT_SERVER_PATH, type VideoStreamPacket } from '../options/index.js';
import type { AdbScrcpyOptions } from './options/index.js';
@@ -255,8 +255,8 @@ export class AdbScrcpyClient {
    private _videoStream: ReadableStream<VideoStreamPacket>;
    public get videoStream() { return this._videoStream; }

    private _controlMessageStream: WritableStream<ScrcpyControlMessage> | undefined;
    public get controlMessageStream() { return this._controlMessageStream; }
    private _controlMessageSerializer: ScrcpyControlMessageSerializer | undefined;
    public get controlMessageSerializer() { return this._controlMessageSerializer; }

    private _deviceMessageStream: ReadableStream<ScrcpyDeviceMessage> | undefined;
    public get deviceMessageStream() { return this._deviceMessageStream; }
@@ -281,7 +281,7 @@ export class AdbScrcpyClient {
            }));

        if (controlStream) {
            this._controlMessageStream = pipeFrom(controlStream.writable, new ScrcpyControlMessageSerializeStream(options));
            this._controlMessageSerializer = new ScrcpyControlMessageSerializer(controlStream.writable, options);
            this._deviceMessageStream = controlStream.readable.pipeThrough(new ScrcpyDeviceMessageDeserializeStream());
        }
    }
+1 −1
Original line number Diff line number Diff line
@@ -2,5 +2,5 @@ export * from './inject-keycode.js';
export * from './inject-text.js';
export * from './inject-touch.js';
export * from './rotate-device.js';
export * from './stream.js';
export * from './serializer.js';
export * from './type.js';
+78 −0
Original line number Diff line number Diff line
import type { WritableStreamDefaultWriter } from '@yume-chan/stream-extra';

import type { ScrcpyInjectScrollControlMessage1_22, ScrcpyOptions } from '../options/index.js';
import { AndroidKeyEventAction, ScrcpyInjectKeyCodeControlMessage } from './inject-keycode.js';
import { ScrcpyInjectTextControlMessage } from './inject-text.js';
import { ScrcpyInjectTouchControlMessage } from './inject-touch.js';
import { ScrcpyRotateDeviceControlMessage } from './rotate-device.js';
import { ScrcpyControlMessageType } from './type.js';

export class ScrcpyControlMessageSerializer {
    private options: ScrcpyOptions<any>;
    /** Control message type values for current version of server */
    private types: ScrcpyControlMessageType[];
    private writer: WritableStreamDefaultWriter<Uint8Array>;

    public constructor(stream: WritableStream<Uint8Array>, options: ScrcpyOptions<any>) {
        this.options = options;
        this.types = options.getControlMessageTypes();
        this.writer = stream.getWriter();
    }

    public getTypeValue(type: ScrcpyControlMessageType): number {
        const value = this.types.indexOf(type);
        if (value === -1) {
            throw new Error('Not supported');
        }
        return value;
    }

    public injectKeyCode(message: Omit<ScrcpyInjectKeyCodeControlMessage, 'type'>) {
        return this.writer.write(ScrcpyInjectKeyCodeControlMessage.serialize({
            ...message,
            type: this.getTypeValue(ScrcpyControlMessageType.InjectKeyCode),
        }));
    }

    public injectText(text: string) {
        return this.writer.write(ScrcpyInjectTextControlMessage.serialize({
            text,
            type: this.getTypeValue(ScrcpyControlMessageType.InjectText),
        }));
    }

    public injectTouch(message: Omit<ScrcpyInjectTouchControlMessage, 'type'>) {
        return this.writer.write(ScrcpyInjectTouchControlMessage.serialize({
            ...message,
            type: this.getTypeValue(ScrcpyControlMessageType.InjectTouch),
        }));
    }

    public injectScroll(message: Omit<ScrcpyInjectScrollControlMessage1_22, 'type'>) {
        return this.writer.write(this.options.serializeInjectScrollControlMessage({
            ...message,
            type: this.getTypeValue(ScrcpyControlMessageType.InjectScroll),
        }));
    }

    public async backOrScreenOn(action: AndroidKeyEventAction) {
        const buffer = this.options.serializeBackOrScreenOnControlMessage({
            action,
            type: this.getTypeValue(ScrcpyControlMessageType.BackOrScreenOn),
        });

        if (buffer) {
            return await this.writer.write(buffer);
        }
    }

    public rotateDevice() {
        return this.writer.write(ScrcpyRotateDeviceControlMessage.serialize({
            type: this.getTypeValue(ScrcpyControlMessageType.RotateDevice),
        }));
    }

    public close() {
        return this.writer.close();
    }
};
+0 −79
Original line number Diff line number Diff line
import { TransformStream } from '@yume-chan/stream-extra';

import type { ScrcpyBackOrScreenOnControlMessage1_18, ScrcpyInjectScrollControlMessage1_22, ScrcpyOptions } from '../options/index.js';
import { ScrcpyInjectKeyCodeControlMessage } from './inject-keycode.js';
import { ScrcpyInjectTextControlMessage } from './inject-text.js';
import { ScrcpyInjectTouchControlMessage } from './inject-touch.js';
import { ScrcpyRotateDeviceControlMessage } from './rotate-device.js';
import { ScrcpyControlMessageType } from './type.js';

export type ScrcpyControlMessage =
    | ScrcpyInjectKeyCodeControlMessage
    | ScrcpyInjectTextControlMessage
    | ScrcpyInjectTouchControlMessage
    | ScrcpyInjectScrollControlMessage1_22
    | ScrcpyBackOrScreenOnControlMessage1_18
    | ScrcpyRotateDeviceControlMessage;

export class ScrcpyControlMessageSerializeStream extends TransformStream<ScrcpyControlMessage, Uint8Array> {
    public constructor(options: ScrcpyOptions<any>) {
        // Get control message types for current version of server
        const types = options.getControlMessageTypes();

        super({
            transform(message, controller) {
                const type = types.indexOf(message.type);
                if (type === -1) {
                    throw new Error('Not supported');
                }

                switch (message.type) {
                    case ScrcpyControlMessageType.InjectKeyCode:
                        controller.enqueue(ScrcpyInjectKeyCodeControlMessage.serialize({
                            ...message,
                            type
                        }));
                        break;
                    case ScrcpyControlMessageType.InjectText:
                        controller.enqueue(ScrcpyInjectTextControlMessage.serialize({
                            ...message,
                            type,
                        }));
                        break;
                    case ScrcpyControlMessageType.InjectTouch:
                        // ADB streams are actually pretty low-bandwidth and laggy
                        // Re-sample move events to avoid flooding the connection

                        controller.enqueue(ScrcpyInjectTouchControlMessage.serialize({
                            ...message,
                            type,
                        }));
                        break;
                    case ScrcpyControlMessageType.InjectScroll:
                        controller.enqueue(options.serializeInjectScrollControlMessage({
                            ...message,
                            type,
                        }))
                        break;
                    case ScrcpyControlMessageType.BackOrScreenOn:
                        {
                            const buffer = options.serializeBackOrScreenOnControlMessage({
                                ...message,
                                type,
                            });

                            if (buffer) {
                                controller.enqueue(buffer);
                            }
                        }
                        break;
                    case ScrcpyControlMessageType.RotateDevice:
                        controller.enqueue(ScrcpyRotateDeviceControlMessage.serialize({
                            type,
                        }));
                        break;
                }
            }
        })
    }
}
Loading