mirror of
https://github.com/viarotel-org/escrcpy.git
synced 2024-11-14 18:57:40 +01:00
perf: ♻️ Optimize terminal performance
This commit is contained in:
parent
5b6b8d1150
commit
d3afc4ba62
@ -1,5 +1,5 @@
|
|||||||
import util from 'node:util'
|
import util from 'node:util'
|
||||||
import child_process from 'node:child_process'
|
import { exec as _exec, spawn } from 'node:child_process'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@ -8,7 +8,7 @@ import appStore from '@electron/helpers/store.js'
|
|||||||
import { adbPath } from '@electron/configs/index.js'
|
import { adbPath } from '@electron/configs/index.js'
|
||||||
import { uniq } from 'lodash-es'
|
import { uniq } from 'lodash-es'
|
||||||
|
|
||||||
const exec = util.promisify(child_process.exec)
|
const exec = util.promisify(_exec)
|
||||||
|
|
||||||
let client = null
|
let client = null
|
||||||
|
|
||||||
@ -37,7 +37,66 @@ appStore.onDidChange('common.adbPath', async (value, oldValue) => {
|
|||||||
client = Adb.createClient({ bin: value || adbPath })
|
client = Adb.createClient({ bin: value || adbPath })
|
||||||
})
|
})
|
||||||
|
|
||||||
const shell = async command => exec(`${adbPath} ${command}`)
|
const shell = async (command) => {
|
||||||
|
const execPath = appStore.get('common.adbPath') || adbPath
|
||||||
|
return exec(`${execPath} ${command}`, {
|
||||||
|
env: { ...process.env },
|
||||||
|
shell: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const spawnShell = async (command, { stdout, stderr } = {}) => {
|
||||||
|
const spawnPath = appStore.get('common.adbPath') || adbPath
|
||||||
|
const args = command.split(' ')
|
||||||
|
|
||||||
|
console.log('adb.spawnShell.spawnPath', spawnPath)
|
||||||
|
console.log('adb.spawnShell.args', args)
|
||||||
|
|
||||||
|
const spawnProcess = spawn(`"${spawnPath}"`, args, {
|
||||||
|
env: { ...process.env },
|
||||||
|
shell: true,
|
||||||
|
encoding: 'utf8',
|
||||||
|
})
|
||||||
|
|
||||||
|
spawnProcess.stdout.on('data', (data) => {
|
||||||
|
const stringData = data.toString()
|
||||||
|
|
||||||
|
console.log('spawnProcess.stdout.data:', stringData)
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
stdout(stringData, spawnProcess)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let lastStderr = ''
|
||||||
|
spawnProcess.stderr.on('data', (data) => {
|
||||||
|
const stringData = data.toString()
|
||||||
|
|
||||||
|
lastStderr = stringData
|
||||||
|
|
||||||
|
console.error('spawnProcess.stderr.data:', stringData)
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
stderr(stringData, spawnProcess)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
spawnProcess.on('close', (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reject(new Error(lastStderr || `Command failed with code ${code}`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
spawnProcess.on('error', (err) => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getDevices = async () => client.listDevicesWithPaths()
|
const getDevices = async () => client.listDevicesWithPaths()
|
||||||
const deviceShell = async (id, command) => {
|
const deviceShell = async (id, command) => {
|
||||||
const res = await client.getDevice(id).shell(command).then(Adb.util.readAll)
|
const res = await client.getDevice(id).shell(command).then(Adb.util.readAll)
|
||||||
@ -193,6 +252,7 @@ export default () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
shell,
|
shell,
|
||||||
|
spawnShell,
|
||||||
getDevices,
|
getDevices,
|
||||||
deviceShell,
|
deviceShell,
|
||||||
kill,
|
kill,
|
||||||
|
@ -46,9 +46,12 @@ const shell = async (command, { debug = false, stdout, stderr } = {}) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let lastStderr = ''
|
||||||
gnirehtetProcess.stderr.on('data', (data) => {
|
gnirehtetProcess.stderr.on('data', (data) => {
|
||||||
const stringData = data.toString()
|
const stringData = data.toString()
|
||||||
|
|
||||||
|
lastStderr = stringData
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.error(`${command}.gnirehtet.process.stderr.data:`, stringData)
|
console.error(`${command}.gnirehtet.process.stderr.data:`, stringData)
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,12 @@ const shell = async (command, { stdout, stderr } = {}) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let lastStderr = ''
|
||||||
scrcpyProcess.stderr.on('data', (data) => {
|
scrcpyProcess.stderr.on('data', (data) => {
|
||||||
const stringData = data.toString()
|
const stringData = data.toString()
|
||||||
|
|
||||||
|
lastStderr = stringData
|
||||||
|
|
||||||
console.error('scrcpyProcess.stderr.data:', stringData)
|
console.error('scrcpyProcess.stderr.data:', stringData)
|
||||||
|
|
||||||
if (stderr) {
|
if (stderr) {
|
||||||
@ -49,7 +52,7 @@ const shell = async (command, { stdout, stderr } = {}) => {
|
|||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
reject(new Error(`Command failed with code ${code}`))
|
reject(new Error(lastStderr || `Command failed with code ${code}`))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import { debounce } from 'lodash-es'
|
||||||
|
import { createStderr, createStdout, textFormatter } from 'vue-command'
|
||||||
|
import { useFixCursor } from './helper.js'
|
||||||
|
|
||||||
|
const $adb = window.adbkit
|
||||||
|
|
||||||
|
export function useAdb({ vShell, history, loading } = {}) {
|
||||||
|
const adb = async (args) => {
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
const command = args.slice(1).join(' ')
|
||||||
|
|
||||||
|
const appendToHistory = debounce(vShell.value.appendToHistory, 500)
|
||||||
|
|
||||||
|
let stdoutText = ''
|
||||||
|
let stderrText = ''
|
||||||
|
$adb.spawnShell(command, {
|
||||||
|
stdout(text) {
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
|
stdoutText += text
|
||||||
|
|
||||||
|
useFixCursor(history)
|
||||||
|
|
||||||
|
appendToHistory(createStdout(stdoutText))
|
||||||
|
},
|
||||||
|
stderr(text) {
|
||||||
|
loading.value = false
|
||||||
|
|
||||||
|
stderrText += text
|
||||||
|
|
||||||
|
useFixCursor(history)
|
||||||
|
|
||||||
|
appendToHistory(createStderr(stderrText))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return textFormatter('Waiting...')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
adb,
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,9 @@
|
|||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import { createStderr, createStdout, textFormatter } from 'vue-command'
|
import { createStderr, createStdout, textFormatter } from 'vue-command'
|
||||||
|
import { useFixCursor } from './helper.js'
|
||||||
|
|
||||||
const $gnirehtet = window.gnirehtet
|
const $gnirehtet = window.gnirehtet
|
||||||
|
|
||||||
const fixCursor = (history) => {
|
|
||||||
const length = history.value.length
|
|
||||||
if (history.value[length - 1]?.__name === 'VueCommandQuery') {
|
|
||||||
history.value.splice(length - 1, 1, textFormatter('Waiting...'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useGnirehtet({ vShell, history, loading } = {}) {
|
export function useGnirehtet({ vShell, history, loading } = {}) {
|
||||||
const gnirehtet = async (args) => {
|
const gnirehtet = async (args) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -26,7 +20,7 @@ export function useGnirehtet({ vShell, history, loading } = {}) {
|
|||||||
|
|
||||||
stdoutText += text
|
stdoutText += text
|
||||||
|
|
||||||
fixCursor(history)
|
useFixCursor(history)
|
||||||
|
|
||||||
appendToHistory(createStdout(stdoutText))
|
appendToHistory(createStdout(stdoutText))
|
||||||
},
|
},
|
||||||
@ -35,13 +29,13 @@ export function useGnirehtet({ vShell, history, loading } = {}) {
|
|||||||
|
|
||||||
stderrText += text
|
stderrText += text
|
||||||
|
|
||||||
fixCursor(history)
|
useFixCursor(history)
|
||||||
|
|
||||||
appendToHistory(createStderr(stderrText))
|
appendToHistory(createStderr(stderrText))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return textFormatter('Loading...')
|
return textFormatter('Waiting...')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
import { textFormatter } from 'vue-command'
|
||||||
|
|
||||||
|
export function useFixCursor(history) {
|
||||||
|
const length = history.value.length
|
||||||
|
if (history.value[length - 1]?.__name === 'VueCommandQuery') {
|
||||||
|
history.value.splice(length - 1, 1, textFormatter('Waiting...'))
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,9 @@
|
|||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import { createStderr, createStdout, textFormatter } from 'vue-command'
|
import { createStderr, createStdout, textFormatter } from 'vue-command'
|
||||||
|
import { useFixCursor } from './helper.js'
|
||||||
|
|
||||||
const $scrcpy = window.scrcpy
|
const $scrcpy = window.scrcpy
|
||||||
|
|
||||||
const fixCursor = (history) => {
|
|
||||||
const length = history.value.length
|
|
||||||
if (history.value[length - 1]?.__name === 'VueCommandQuery') {
|
|
||||||
history.value.splice(length - 1, 1, textFormatter('Waiting...'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useScrcpy({ vShell, history, loading } = {}) {
|
export function useScrcpy({ vShell, history, loading } = {}) {
|
||||||
const scrcpy = async (args) => {
|
const scrcpy = async (args) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -22,11 +16,12 @@ export function useScrcpy({ vShell, history, loading } = {}) {
|
|||||||
let stderrText = ''
|
let stderrText = ''
|
||||||
$scrcpy.shell(command, {
|
$scrcpy.shell(command, {
|
||||||
stdout(text) {
|
stdout(text) {
|
||||||
|
console.log('history', history)
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|
||||||
stdoutText += text
|
stdoutText += text
|
||||||
|
|
||||||
fixCursor(history)
|
useFixCursor(history)
|
||||||
|
|
||||||
appendToHistory(createStdout(stdoutText))
|
appendToHistory(createStdout(stdoutText))
|
||||||
},
|
},
|
||||||
@ -35,13 +30,13 @@ export function useScrcpy({ vShell, history, loading } = {}) {
|
|||||||
|
|
||||||
stderrText += text
|
stderrText += text
|
||||||
|
|
||||||
fixCursor(history)
|
useFixCursor(history)
|
||||||
|
|
||||||
appendToHistory(createStderr(stderrText))
|
appendToHistory(createStderr(stderrText))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return textFormatter('Loading...')
|
return textFormatter('Waiting...')
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -3,32 +3,34 @@
|
|||||||
v-model="visible"
|
v-model="visible"
|
||||||
width="80%"
|
width="80%"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
:close-on-press-escape="false"
|
:close-on-press-escape="true"
|
||||||
class="overflow-hidden rounded-xl el-dialog-headless"
|
class="overflow-hidden rounded-md el-dialog-headless dark:border dark:border-gray-700"
|
||||||
@open="onOpen"
|
@open="onOpen"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon
|
||||||
class="cursor-pointer absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-gray-200 hover:bg-gray-700 !active:bg-red-600 rounded"
|
class="cursor-pointer absolute top-2 right-2 w-8 h-8 flex items-center justify-center hover:bg-gray-200 dark:text-gray-200 dark:hover:bg-gray-700 !active:bg-red-600 !active:text-gray-200 rounded-md"
|
||||||
@click="hide"
|
@click="hide"
|
||||||
>
|
>
|
||||||
<CloseBold />
|
<CloseBold />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
|
|
||||||
<VueCommand
|
<VueCommand
|
||||||
v-if="visible"
|
v-if="renderShell"
|
||||||
:ref="(value) => (vShell = value)"
|
:ref="(value) => (vShell = value)"
|
||||||
v-model:history="history"
|
v-model:history="history"
|
||||||
|
:dispatched-queries="dispatchedQueries"
|
||||||
:commands="commands"
|
:commands="commands"
|
||||||
hide-bar
|
hide-bar
|
||||||
show-help
|
show-help
|
||||||
help-text="Type in help"
|
help-text="Type in help"
|
||||||
:help-timeout="3500"
|
:help-timeout="3000"
|
||||||
|
:invert="invert"
|
||||||
class=""
|
class=""
|
||||||
:dispatched-queries="dispatchedQueries"
|
@update:dispatched-queries="onDispatchedQueriesUpdate"
|
||||||
>
|
>
|
||||||
<template #prompt>
|
<template #prompt>
|
||||||
<div class="flex items-center pr-2">
|
<div class="flex items-center pr-2">
|
||||||
<span class="">escrcpy~$</span>
|
<span class="">Escrcpy~$</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VueCommand>
|
</VueCommand>
|
||||||
@ -36,7 +38,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref } from 'vue'
|
import { ref, shallowRef } from 'vue'
|
||||||
import VueCommand, {
|
import VueCommand, {
|
||||||
createQuery,
|
createQuery,
|
||||||
createStdout,
|
createStdout,
|
||||||
@ -53,8 +55,9 @@ export default {
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const vShell = ref(null)
|
const vShell = ref(null)
|
||||||
const history = ref([createQuery()])
|
const history = shallowRef([createQuery()])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const renderShell = ref(false)
|
||||||
const dispatchedQueries = ref(new Set([]))
|
const dispatchedQueries = ref(new Set([]))
|
||||||
|
|
||||||
const { adb } = useAdb({ vShell, history, loading })
|
const { adb } = useAdb({ vShell, history, loading })
|
||||||
@ -76,7 +79,17 @@ export default {
|
|||||||
return createStdout(listFormatter('Supported Commands:', ...commandList))
|
return createStdout(listFormatter('Supported Commands:', ...commandList))
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchedQueries.value = new Set(Object.keys(commands.value))
|
dispatchedQueries.value = new Set([
|
||||||
|
...(window.appStore.get('terminal.dispatchedQueries') || []),
|
||||||
|
...Object.keys(commands.value),
|
||||||
|
])
|
||||||
|
|
||||||
|
const onOpen = () => {
|
||||||
|
console.log('vShell.value', vShell.value)
|
||||||
|
console.log('history.value', history.value)
|
||||||
|
|
||||||
|
renderShell.value = true
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
vShell,
|
vShell,
|
||||||
@ -84,6 +97,8 @@ export default {
|
|||||||
history,
|
history,
|
||||||
commands,
|
commands,
|
||||||
dispatchedQueries,
|
dispatchedQueries,
|
||||||
|
onOpen,
|
||||||
|
renderShell,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -91,22 +106,44 @@ export default {
|
|||||||
visible: false,
|
visible: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
invert() {
|
||||||
|
return this.$store.theme.value !== 'dark'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'vShell.signals': {
|
||||||
|
handler(value) {
|
||||||
|
console.log('vShell.signals.value', value)
|
||||||
|
|
||||||
|
value.off('SIGINT')
|
||||||
|
|
||||||
|
value.on('SIGINT', () => {
|
||||||
|
console.log('vShell.signals.on.SIGINT')
|
||||||
|
this.onCtrlC()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
show() {
|
show() {
|
||||||
this.visible = true
|
this.visible = true
|
||||||
},
|
},
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.visible = false
|
this.visible = false
|
||||||
},
|
},
|
||||||
async onOpen() {
|
|
||||||
console.log('vShell', this.vShell)
|
|
||||||
|
|
||||||
this.vShell.signals.off('SIGINT')
|
onDispatchedQueriesUpdate(value) {
|
||||||
|
console.log('onDispatchedQueriesUpdate.value', value)
|
||||||
|
|
||||||
this.vShell.signals.on('SIGINT', () => {
|
this.$appStore.set('terminal.dispatchedQueries', Array.from(value))
|
||||||
console.log('vShell.signals.on.SIGINT')
|
|
||||||
this.$gnirehtet.shell('stop')
|
this.dispatchedQueries = value
|
||||||
})
|
},
|
||||||
|
|
||||||
|
onCtrlC() {
|
||||||
|
window.gnirehtet.shell('stop')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -282,15 +282,21 @@ export default {
|
|||||||
args: this.scrcpyArgs(row.id, { isRecord: true }),
|
args: this.scrcpyArgs(row.id, { isRecord: true }),
|
||||||
stdout: this.onStdout,
|
stdout: this.onStdout,
|
||||||
})
|
})
|
||||||
|
this.handleRecordSuccess(savePath)
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.warn(error)
|
console.warn(error)
|
||||||
|
|
||||||
if (error.message) {
|
if (error.message) {
|
||||||
this.$message.warning(error.message)
|
this.$message.warning(error.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleReset()
|
this.handleReset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row.$recordLoading = false
|
||||||
|
},
|
||||||
|
async handleRecordSuccess(savePath) {
|
||||||
try {
|
try {
|
||||||
await this.$confirm(
|
await this.$confirm(
|
||||||
this.$t('device.record.success.message'),
|
this.$t('device.record.success.message'),
|
||||||
@ -311,8 +317,6 @@ export default {
|
|||||||
catch (error) {
|
catch (error) {
|
||||||
console.warn(error)
|
console.warn(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
row.$recordLoading = false
|
|
||||||
},
|
},
|
||||||
async handleMirror(row) {
|
async handleMirror(row) {
|
||||||
row.$loading = true
|
row.$loading = true
|
||||||
|
Loading…
Reference in New Issue
Block a user