mirror of
https://github.com/AkiChase/scrcpy-mask
synced 2025-02-23 07:22:17 +08:00
feat(websocket): add external control
This commit is contained in:
parent
e832abff58
commit
365c915103
@ -40,13 +40,13 @@ import {
|
|||||||
useMessage,
|
useMessage,
|
||||||
NInputGroup,
|
NInputGroup,
|
||||||
} from "naive-ui";
|
} from "naive-ui";
|
||||||
import { CloseCircle, InformationCircle } from "@vicons/ionicons5";
|
import { CloseCircle, InformationCircle, Refresh } from "@vicons/ionicons5";
|
||||||
import { Refresh } from "@vicons/ionicons5";
|
|
||||||
import { UnlistenFn, listen } from "@tauri-apps/api/event";
|
import { UnlistenFn, listen } from "@tauri-apps/api/event";
|
||||||
import { Store } from "@tauri-apps/plugin-store";
|
import { Store } from "@tauri-apps/plugin-store";
|
||||||
import { shutdown } from "../frontcommand/scrcpyMaskCmd";
|
import { shutdown } from "../frontcommand/scrcpyMaskCmd";
|
||||||
import { useGlobalStore } from "../store/global";
|
import { useGlobalStore } from "../store/global";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { closeExternalControl, connectExternalControl } from "../websocket";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
@ -54,7 +54,8 @@ const store = useGlobalStore();
|
|||||||
const message = useMessage();
|
const message = useMessage();
|
||||||
|
|
||||||
const port = ref(27183);
|
const port = ref(27183);
|
||||||
const address = ref("");
|
const wireless_address = ref("");
|
||||||
|
const ws_address = ref("");
|
||||||
|
|
||||||
const localStore = new Store("store.bin");
|
const localStore = new Store("store.bin");
|
||||||
|
|
||||||
@ -300,15 +301,29 @@ async function refreshDevices() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function connectDevice() {
|
async function connectDevice() {
|
||||||
if (!address.value) {
|
if (!wireless_address.value) {
|
||||||
message.error(t("pages.Device.inputWirelessAddress"));
|
message.error(t("pages.Device.inputWirelessAddress"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
store.showLoading();
|
store.showLoading();
|
||||||
message.info(await adbConnect(address.value));
|
message.info(await adbConnect(wireless_address.value));
|
||||||
await refreshDevices();
|
await refreshDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connectWS() {
|
||||||
|
if (!ws_address.value) {
|
||||||
|
message.error(t("pages.Device.inputWsAddress"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
store.showLoading();
|
||||||
|
connectExternalControl(ws_address.value, message, store, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWS() {
|
||||||
|
closeExternalControl();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -327,7 +342,7 @@ async function connectDevice() {
|
|||||||
<NH4 prefix="bar">{{ $t("pages.Device.wireless") }}</NH4>
|
<NH4 prefix="bar">{{ $t("pages.Device.wireless") }}</NH4>
|
||||||
<NInputGroup style="max-width: 300px">
|
<NInputGroup style="max-width: 300px">
|
||||||
<NInput
|
<NInput
|
||||||
v-model:value="address"
|
v-model:value="wireless_address"
|
||||||
clearable
|
clearable
|
||||||
:placeholder="$t('pages.Device.wirelessPlaceholder')"
|
:placeholder="$t('pages.Device.wirelessPlaceholder')"
|
||||||
/>
|
/>
|
||||||
@ -335,6 +350,24 @@ async function connectDevice() {
|
|||||||
$t("pages.Device.connect")
|
$t("pages.Device.connect")
|
||||||
}}</NButton>
|
}}</NButton>
|
||||||
</NInputGroup>
|
</NInputGroup>
|
||||||
|
<NH4 prefix="bar">{{ $t("pages.Device.externalControl") }}</NH4>
|
||||||
|
<NInputGroup style="max-width: 300px">
|
||||||
|
<NInput
|
||||||
|
v-model:value="ws_address"
|
||||||
|
clearable
|
||||||
|
:placeholder="$t('pages.Device.wsAddress')"
|
||||||
|
:disabled="store.externalControlled"
|
||||||
|
/>
|
||||||
|
<NButton
|
||||||
|
v-if="store.externalControlled"
|
||||||
|
type="error"
|
||||||
|
@click="closeWS"
|
||||||
|
>{{ $t("pages.Device.wsClose") }}</NButton
|
||||||
|
>
|
||||||
|
<NButton v-else type="primary" @click="connectWS">{{
|
||||||
|
$t("pages.Device.wsConnect")
|
||||||
|
}}</NButton>
|
||||||
|
</NInputGroup>
|
||||||
<NH4 prefix="bar">{{ $t("pages.Device.deviceSize.title") }}</NH4>
|
<NH4 prefix="bar">{{ $t("pages.Device.deviceSize.title") }}</NH4>
|
||||||
<NFlex justify="left" align="center">
|
<NFlex justify="left" align="center">
|
||||||
<NFormItem :label="$t('pages.Device.deviceSize.width')">
|
<NFormItem :label="$t('pages.Device.deviceSize.width')">
|
||||||
|
@ -37,7 +37,12 @@
|
|||||||
"availableDevice": "Available devices",
|
"availableDevice": "Available devices",
|
||||||
"noControledDevice": "No Controled Device",
|
"noControledDevice": "No Controled Device",
|
||||||
"alreadyControled": "Controlled device already exists",
|
"alreadyControled": "Controlled device already exists",
|
||||||
"alreadyDisconnected": "Controlled device connection has been disconnected"
|
"alreadyDisconnected": "Controlled device connection has been disconnected",
|
||||||
|
"externalControl": "External control",
|
||||||
|
"wsAddress": "Websocket address",
|
||||||
|
"inputWsAddress": "Please enter the Websocket address",
|
||||||
|
"wsClose": "Close",
|
||||||
|
"wsConnect": "Control"
|
||||||
},
|
},
|
||||||
"Mask": {
|
"Mask": {
|
||||||
"inputBoxPlaceholder": "Input text and then press enter/esc",
|
"inputBoxPlaceholder": "Input text and then press enter/esc",
|
||||||
@ -218,5 +223,10 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"noControledDevice": "No devices are controlled"
|
"noControledDevice": "No devices are controlled"
|
||||||
|
},
|
||||||
|
"websocket": {
|
||||||
|
"open": "Connected to external control server",
|
||||||
|
"close": "External control connection disconnected",
|
||||||
|
"error": "Something was wrong, the exter connection is closed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ const localStore = new Store("store.bin");
|
|||||||
|
|
||||||
const i18n = createI18n({
|
const i18n = createI18n({
|
||||||
allowComposition: true,
|
allowComposition: true,
|
||||||
|
legacy: false,
|
||||||
messages: {
|
messages: {
|
||||||
"en-US": enUS,
|
"en-US": enUS,
|
||||||
"zh-CN": zhCN,
|
"zh-CN": zhCN,
|
||||||
@ -15,7 +16,7 @@ const i18n = createI18n({
|
|||||||
});
|
});
|
||||||
|
|
||||||
localStore.get<"en-US" | "zh-CN">("language").then((language) => {
|
localStore.get<"en-US" | "zh-CN">("language").then((language) => {
|
||||||
i18n.global.locale = language ?? "en-US";
|
i18n.global.locale.value = language ?? "en-US";
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
@ -37,7 +37,12 @@
|
|||||||
"noControledDevice": "无受控设备",
|
"noControledDevice": "无受控设备",
|
||||||
"availableDevice": "可用设备",
|
"availableDevice": "可用设备",
|
||||||
"alreadyControled": "已存在受控设备",
|
"alreadyControled": "已存在受控设备",
|
||||||
"alreadyDisconnected": "受控设备连接已断开"
|
"alreadyDisconnected": "受控设备连接已断开",
|
||||||
|
"inputWsAddress": "请输入 Websocket 地址",
|
||||||
|
"externalControl": "外部控制",
|
||||||
|
"wsAddress": "Websocket 地址",
|
||||||
|
"wsClose": "断开",
|
||||||
|
"wsConnect": "控制"
|
||||||
},
|
},
|
||||||
"Mask": {
|
"Mask": {
|
||||||
"keyconfigException": "按键方案异常,请删除此方案",
|
"keyconfigException": "按键方案异常,请删除此方案",
|
||||||
@ -218,5 +223,10 @@
|
|||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"noControledDevice": "未控制任何设备"
|
"noControledDevice": "未控制任何设备"
|
||||||
|
},
|
||||||
|
"websocket": {
|
||||||
|
"open": "已连接到外部控制服务端",
|
||||||
|
"close": "外部控制连接断开",
|
||||||
|
"error": "未知错误,外部控制连接断开"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,8 @@ export const useGlobalStore = defineStore("global", () => {
|
|||||||
localStore.set("curKeyMappingIndex", index);
|
localStore.set("curKeyMappingIndex", index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const externalControlled = ref(false);
|
||||||
|
|
||||||
// persistent storage
|
// persistent storage
|
||||||
const screenSizeW: Ref<number> = ref(0);
|
const screenSizeW: Ref<number> = ref(0);
|
||||||
const screenSizeH: Ref<number> = ref(0);
|
const screenSizeH: Ref<number> = ref(0);
|
||||||
@ -88,6 +90,7 @@ export const useGlobalStore = defineStore("global", () => {
|
|||||||
curKeyMappingIndex,
|
curKeyMappingIndex,
|
||||||
maskButton,
|
maskButton,
|
||||||
checkUpdateAtStart,
|
checkUpdateAtStart,
|
||||||
|
externalControlled,
|
||||||
// in-memory storage
|
// in-memory storage
|
||||||
showLoading,
|
showLoading,
|
||||||
hideLoading,
|
hideLoading,
|
||||||
|
81
src/websocket.ts
Normal file
81
src/websocket.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { useMessage } from "naive-ui";
|
||||||
|
import { useGlobalStore } from "./store/global";
|
||||||
|
import { sendKey, shutdown, swipe, touch } from "./frontcommand/scrcpyMaskCmd";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
|
let ws: WebSocket;
|
||||||
|
let sharedMessage: ReturnType<typeof useMessage>;
|
||||||
|
let sharedStore: ReturnType<typeof useGlobalStore>;
|
||||||
|
let t: ReturnType<typeof useI18n>["t"];
|
||||||
|
|
||||||
|
export function connectExternalControl(
|
||||||
|
url: string,
|
||||||
|
message: ReturnType<typeof useMessage>,
|
||||||
|
store: ReturnType<typeof useGlobalStore>,
|
||||||
|
i18nT: ReturnType<typeof useI18n>["t"]
|
||||||
|
) {
|
||||||
|
sharedMessage = message;
|
||||||
|
sharedStore = store;
|
||||||
|
t = i18nT;
|
||||||
|
|
||||||
|
ws = new WebSocket(url);
|
||||||
|
ws.addEventListener("open", handleOpen);
|
||||||
|
ws.addEventListener("message", handleMessage);
|
||||||
|
ws.addEventListener("close", handleClose);
|
||||||
|
ws.addEventListener("error", handleError);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function closeExternalControl() {
|
||||||
|
if (ws) ws.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpen() {
|
||||||
|
sharedStore.externalControlled = true;
|
||||||
|
sharedStore.hideLoading();
|
||||||
|
sharedMessage.success(t("websocket.open"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleMessage(event: MessageEvent) {
|
||||||
|
try {
|
||||||
|
const msg = JSON.parse(event.data);
|
||||||
|
if (msg.type === "showMessage") {
|
||||||
|
sharedMessage.create(msg.msgContent, { type: msg.msgType });
|
||||||
|
} else if (msg.type === "getControlledDevice") {
|
||||||
|
msg.controledDevice = sharedStore.controledDevice;
|
||||||
|
ws.send(JSON.stringify(msg));
|
||||||
|
} else if (msg.type === "sendKey") {
|
||||||
|
delete msg.type;
|
||||||
|
await sendKey(msg);
|
||||||
|
} else if (msg.type === "touch") {
|
||||||
|
msg.screen = { w: sharedStore.screenSizeW, h: sharedStore.screenSizeH };
|
||||||
|
delete msg.type;
|
||||||
|
await touch(msg);
|
||||||
|
} else if (msg.type === "swipe") {
|
||||||
|
console.log(msg);
|
||||||
|
msg.screen = { w: sharedStore.screenSizeW, h: sharedStore.screenSizeH };
|
||||||
|
delete msg.type;
|
||||||
|
await swipe(msg);
|
||||||
|
} else if (msg.type === "shutdown") {
|
||||||
|
await shutdown();
|
||||||
|
sharedStore.controledDevice = null;
|
||||||
|
} else {
|
||||||
|
console.error("Invalid message received", msg);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Message received failed", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
sharedMessage.info(t("websocket.close"));
|
||||||
|
ws.close();
|
||||||
|
sharedStore.externalControlled = false;
|
||||||
|
sharedStore.hideLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError() {
|
||||||
|
sharedMessage.error(t("websocket.error"));
|
||||||
|
ws.close();
|
||||||
|
sharedStore.externalControlled = false;
|
||||||
|
sharedStore.hideLoading();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user