From ad8913f3c5bcd8b505281e9cfebc11955edc993b Mon Sep 17 00:00:00 2001 From: Kenta420 Date: Wed, 24 Jan 2024 13:59:04 +0700 Subject: [PATCH] update scrcpy --- client-electron/.prettierrc | 2 +- client-electron/src/hooks/adbStore.ts | 26 --- client-electron/src/pages/Android.tsx | 243 ++++++++++++-------------- 3 files changed, 109 insertions(+), 162 deletions(-) delete mode 100644 client-electron/src/hooks/adbStore.ts diff --git a/client-electron/.prettierrc b/client-electron/.prettierrc index 596a235..bfd9942 100644 --- a/client-electron/.prettierrc +++ b/client-electron/.prettierrc @@ -6,5 +6,5 @@ "arrowParens": "avoid", "endOfLine": "lf", "tabWidth": 2, - "proseWrap": "preserve" + "printWidth": 150 } \ No newline at end of file diff --git a/client-electron/src/hooks/adbStore.ts b/client-electron/src/hooks/adbStore.ts deleted file mode 100644 index b516708..0000000 --- a/client-electron/src/hooks/adbStore.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { create } from 'zustand' -import { createJSONStorage, persist } from 'zustand/middleware' - -interface AdbStore { - deviceSerial: string - isAlreadyConnected: boolean - setDeviceSerial: (deviceSerial: string) => void - setIsAlreadyConnected: (isAlreadyConnected: boolean) => void -} - -const useAdbStore = create( - persist( - set => ({ - deviceSerial: '', - isAlreadyConnected: false, - setDeviceSerial: deviceSerial => set({ deviceSerial }), - setIsAlreadyConnected: isAlreadyConnected => set({ isAlreadyConnected }) - }), - { - name: 'adb', - storage: createJSONStorage(() => sessionStorage) - } - ) -) - -export default useAdbStore diff --git a/client-electron/src/pages/Android.tsx b/client-electron/src/pages/Android.tsx index 2bac940..e10aecf 100644 --- a/client-electron/src/pages/Android.tsx +++ b/client-electron/src/pages/Android.tsx @@ -7,21 +7,19 @@ import type { AdbScrcpyVideoStream } from '@yume-chan/adb-scrcpy' import { AdbScrcpyClient, AdbScrcpyOptions1_22 } from '@yume-chan/adb-scrcpy' import { WebCodecsDecoder } from '@yume-chan/scrcpy-decoder-webcodecs' import { + AndroidKeyCode, + AndroidKeyEventAction, AndroidMotionEventAction, ScrcpyLogLevel1_18, ScrcpyOptions1_25, ScrcpyPointerId, ScrcpyVideoCodecId } from '@yume-chan/scrcpy' -import { useCallback, useEffect, useRef, useState } from 'react' -import { - ReadableStream, - WritableStream, - Consumable, - DecodeUtf8Stream -} from '@yume-chan/stream-extra' +import { useCallback, useRef, useState } from 'react' +import { ReadableStream, WritableStream, Consumable, DecodeUtf8Stream } from '@yume-chan/stream-extra' import { useBeforeUnload } from 'react-router-dom' -import useAdbStore from '../hooks/adbStore' +import { Button } from '@/components/ui/button' +import { ArrowLeftIcon, HomeIcon } from '@radix-ui/react-icons' const AndroidPage: React.FC = () => { const { adb, manager, device, setAdb, setDevice } = useAdb( @@ -34,60 +32,6 @@ const AndroidPage: React.FC = () => { })) ) - const { - isAlreadyConnected, - deviceSerial, - setIsAlreadyConnected, - setDeviceSerial - } = useAdbStore( - useShallow(state => ({ - isAlreadyConnected: state.isAlreadyConnected, - setIsAlreadyConnected: state.setIsAlreadyConnected, - deviceSerial: state.deviceSerial, - setDeviceSerial: state.setDeviceSerial - })) - ) - - useEffect(() => { - if (isAlreadyConnected) { - const reconnectAdb = async () => { - const device = await manager - ?.getDevices([ - { - ...ADB_DEFAULT_DEVICE_FILTER, - serialNumber: deviceSerial - } - ]) - .then(devices => devices[0]) - - if (!device) { - // clear state - setIsAlreadyConnected(false) - setDeviceSerial('') - return - } - - const connection = await device.connect() - - const credentialStore: AdbWebCredentialStore = - new AdbWebCredentialStore() - - const transport = await AdbDaemonTransport.authenticate({ - serial: deviceSerial, - connection: connection, - credentialStore: credentialStore - }) - - const adb: Adb = new Adb(transport) - - setAdb(adb) - setDevice(device) - } - - reconnectAdb() - } - }, []) - async function createNewConnection() { device?.raw.forget() setDevice(undefined) @@ -118,9 +62,6 @@ const AndroidPage: React.FC = () => { const adb: Adb = new Adb(transport) setAdb(adb) - - setDeviceSerial(selectedDevice.serial) - setIsAlreadyConnected(true) } const [client, setClient] = useState() @@ -130,16 +71,18 @@ const AndroidPage: React.FC = () => { // when user close or refresh the page, close the adb connection useBeforeUnload( useCallback(() => { + decoder?.dispose() client?.close() adb?.close() - device?.raw.forget() + + setDecoder(undefined) + setClient(undefined) + setAdb(undefined) }, []) ) async function scrcpyConnect() { - const server: ArrayBuffer = await fetch( - new URL('../scrcpy/scrcpy_server_v1.25', import.meta.url) - ).then(res => res.arrayBuffer()) + const server: ArrayBuffer = await fetch(new URL('../scrcpy/scrcpy_server_v1.25', import.meta.url)).then(res => res.arrayBuffer()) await AdbScrcpyClient.pushServer( adb!, @@ -151,9 +94,7 @@ const AndroidPage: React.FC = () => { }) ) - const res = await adb!.subprocess.spawn( - 'CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 1.25' - ) + const res = await adb!.subprocess.spawn('CLASSPATH=/data/local/tmp/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 1.25') res.stdout.pipeThrough(new DecodeUtf8Stream()).pipeTo( new WritableStream({ @@ -165,77 +106,90 @@ const AndroidPage: React.FC = () => { const scrcpyOption = new ScrcpyOptions1_25({ maxFps: 60, + bitRate: 4000000, + stayAwake: true, control: true, logLevel: ScrcpyLogLevel1_18.Debug }) - const _client = await AdbScrcpyClient.start( - adb!, - '/data/local/tmp/scrcpy-server.jar', - '1.25', - new AdbScrcpyOptions1_22(scrcpyOption) - ) + const _client = await AdbScrcpyClient.start(adb!, '/data/local/tmp/scrcpy-server.jar', '1.25', new AdbScrcpyOptions1_22(scrcpyOption)) - const videoStream: AdbScrcpyVideoStream | undefined = - await _client?.videoStream + const videoStream: AdbScrcpyVideoStream | undefined = await _client?.videoStream if (videoStream) { const _decoder = new WebCodecsDecoder(ScrcpyVideoCodecId.H264) - _decoder.renderer.style.maxHeight = '900px' + _decoder.renderer.style.width = '100%' + _decoder.renderer.style.height = '100%' + + if (screenRef.current && screenRef.current.firstChild) { + screenRef.current.removeChild(screenRef.current.firstChild) + } screenRef.current?.appendChild(_decoder.renderer) videoStream?.stream.pipeTo(_decoder.writable) setDecoder(_decoder) if (_client.controlMessageWriter) { _decoder.renderer.addEventListener('mousedown', e => { + // client width and height 700 x 400 const react = _decoder.renderer.getBoundingClientRect() - const x = e.clientX - react.left - const y = e.clientY - react.top - console.log('mouse down at ' + x + ' ' + y) + + // normalize to _decoder.renderer.width and height 1080 x 1920 + const x = ((e.clientX - react.left) * _decoder.renderer.width) / react.width + const y = ((e.clientY - react.top) * _decoder.renderer.height) / react.height + + //console.log('mouse down at ' + x + ' ' + y) _client.controlMessageWriter?.injectTouch({ action: AndroidMotionEventAction.Down, - pointerId: ScrcpyPointerId.Mouse, + pointerId: ScrcpyPointerId.Mouse | ScrcpyPointerId.Finger, pointerX: x, pointerY: y, pressure: 1, - screenWidth: _decoder.renderer.clientWidth, - screenHeight: _decoder.renderer.clientHeight, - buttons: 0, - actionButton: 0 - }) - }) - - _decoder.renderer.addEventListener('mousemove', e => { - const react = _decoder.renderer.getBoundingClientRect() - const x = e.clientX - react.left - const y = e.clientY - react.top - console.log('mouse move at ' + x + ' ' + y) - _client.controlMessageWriter?.injectTouch({ - action: AndroidMotionEventAction.Move, - pointerId: ScrcpyPointerId.Mouse, - pointerX: x, - pointerY: y, - pressure: 1, - screenWidth: _decoder.renderer.clientWidth, - screenHeight: _decoder.renderer.clientHeight, + screenWidth: _decoder.renderer.width, + screenHeight: _decoder.renderer.height, buttons: 0, actionButton: 0 }) }) _decoder.renderer.addEventListener('mouseup', e => { + // client width and height 700 x 400 const react = _decoder.renderer.getBoundingClientRect() - const x = e.clientX - react.left - const y = e.clientY - react.top - console.log('mouse up at ' + x + ' ' + y) + + // normalize to _decoder.renderer.width and height 1080 x 1920 + const x = ((e.clientX - react.left) * _decoder.renderer.width) / react.width + const y = ((e.clientY - react.top) * _decoder.renderer.height) / react.height + + //console.log('mouse up at ' + x + ' ' + y) _client.controlMessageWriter?.injectTouch({ action: AndroidMotionEventAction.Up, pointerId: ScrcpyPointerId.Mouse, pointerX: x, pointerY: y, pressure: 1, - screenWidth: _decoder.renderer.clientWidth, - screenHeight: _decoder.renderer.clientHeight, + screenWidth: _decoder.renderer.width, + screenHeight: _decoder.renderer.height, + buttons: 0, + actionButton: 0 + }) + }) + + _decoder.renderer.addEventListener('mousemove', e => { + // client width and height 700 x 400 + const react = _decoder.renderer.getBoundingClientRect() + + // normalize to _decoder.renderer.width and height 1080 x 1920 + const x = ((e.clientX - react.left) * _decoder.renderer.width) / react.width + const y = ((e.clientY - react.top) * _decoder.renderer.height) / react.height + + //console.log('mouse move at ' + x + ' ' + y) + _client.controlMessageWriter?.injectTouch({ + action: AndroidMotionEventAction.Move, + pointerId: ScrcpyPointerId.Mouse, + pointerX: x, + pointerY: y, + pressure: 1, + screenWidth: _decoder.renderer.width, + screenHeight: _decoder.renderer.height, buttons: 0, actionButton: 0 }) @@ -251,9 +205,6 @@ const AndroidPage: React.FC = () => { adb?.close() setClient(undefined) setAdb(undefined) - setIsAlreadyConnected(false) - setDeviceSerial('') - device?.raw.forget() } function scrcpyDisconnect() { @@ -271,40 +222,62 @@ const AndroidPage: React.FC = () => { console.log('[rebootDevice] res: ', res) } - async function killScrcpyServer() { - const res = await adb?.subprocess.spawn( - 'kill -9 $(pidof -s com.genymobile.scrcpy.Server)' - ) - res?.stdout.pipeThrough(new DecodeUtf8Stream()).pipeTo( - new WritableStream({ - write(chunk) { - console.log(chunk) - } - }) - ) + function goHome() { + client?.controlMessageWriter?.injectKeyCode({ + action: AndroidKeyEventAction.Up, + keyCode: AndroidKeyCode.AndroidHome, + metaState: 0, + repeat: 0 + }) + } + + function goBack() { + client?.controlMessageWriter?.injectKeyCode({ + action: AndroidKeyEventAction.Up, + keyCode: AndroidKeyCode.AndroidBack, + metaState: 0, + repeat: 0 + }) } return ( -
-
-
-
+
+
+
+
+ {screenRef.current?.firstChild ? ( +
+ + +
+ ) : null} +
+
+ - {adb ? : ''} - {adb ? : ''}
-
{adb && device ? device.name : ''}
)