{{ store.controledDevice.deviceName }} ({{
- store.controledDevice.device.id
+ store.controledDevice.deviceID
}})
@@ -344,8 +410,7 @@ async function connectDevice() {
- scid: {{ store.controledDevice.scid }}
status:
- {{ store.controledDevice.device.status }}
+ scid: {{ store.controledDevice.scid }}
diff --git a/src/components/Mask.vue b/src/components/Mask.vue
index 639ed5c..6d2611b 100644
--- a/src/components/Mask.vue
+++ b/src/components/Mask.vue
@@ -14,18 +14,12 @@ import { KeyMappingConfig, KeySteeringWheel } from "../keyMappingConfig";
import { getVersion } from "@tauri-apps/api/app";
import { fetch } from "@tauri-apps/plugin-http";
import { open } from "@tauri-apps/plugin-shell";
-import {
- sendInjectKeycode,
- sendSetClipboard,
-} from "../frontcommand/controlMsg";
+import { sendSetClipboard } from "../frontcommand/controlMsg";
import { getCurrent, PhysicalSize } from "@tauri-apps/api/window";
-import {
- AndroidKeyEventAction,
- AndroidKeycode,
- AndroidMetastate,
-} from "../frontcommand/android";
+import { AndroidKeycode } from "../frontcommand/android";
import { Store } from "@tauri-apps/plugin-store";
import { useI18n } from "vue-i18n";
+import { SendKeyAction, sendKey } from "../frontcommand/scrcpyMaskCmd";
const { t } = useI18n();
const store = useGlobalStore();
@@ -203,18 +197,9 @@ async function pasteText() {
});
await sleep(300);
// send enter
- await sendInjectKeycode({
- action: AndroidKeyEventAction.AKEY_EVENT_ACTION_DOWN,
+ await sendKey({
+ action: SendKeyAction.Default,
keycode: AndroidKeycode.AKEYCODE_ENTER,
- repeat: 0,
- metastate: AndroidMetastate.AMETA_NONE,
- });
- await sleep(50);
- await sendInjectKeycode({
- action: AndroidKeyEventAction.AKEY_EVENT_ACTION_UP,
- keycode: AndroidKeycode.AKEYCODE_ENTER,
- repeat: 0,
- metastate: AndroidMetastate.AMETA_NONE,
});
}
diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue
index c169f44..6812dda 100644
--- a/src/components/Sidebar.vue
+++ b/src/components/Sidebar.vue
@@ -15,17 +15,11 @@ import {
import { Keyboard24Regular } from "@vicons/fluent";
import { NIcon, useMessage } from "naive-ui";
import { useGlobalStore } from "../store/global";
-import {
- sendInjectKeycode,
- sendSetScreenPowerMode,
-} from "../frontcommand/controlMsg";
-import {
- AndroidKeyEventAction,
- AndroidKeycode,
- AndroidMetastate,
-} from "../frontcommand/android";
+import { sendSetScreenPowerMode } from "../frontcommand/controlMsg";
+import { AndroidKeycode } from "../frontcommand/android";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
+import { SendKeyAction, sendKey } from "../frontcommand/scrcpyMaskCmd";
const { t } = useI18n();
const router = useRouter();
@@ -39,28 +33,11 @@ function nav(name: string) {
router.replace({ name });
}
-function sleep(time: number) {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve();
- }, time);
- });
-}
-
async function sendKeyCodeToDevice(code: AndroidKeycode) {
if (store.controledDevice) {
- await sendInjectKeycode({
- action: AndroidKeyEventAction.AKEY_EVENT_ACTION_DOWN,
+ await sendKey({
+ action: SendKeyAction.Default,
keycode: code,
- repeat: 0,
- metastate: AndroidMetastate.AMETA_NONE,
- });
- await sleep(50);
- await sendInjectKeycode({
- action: AndroidKeyEventAction.AKEY_EVENT_ACTION_UP,
- keycode: code,
- repeat: 0,
- metastate: AndroidMetastate.AMETA_NONE,
});
} else {
message.error(t("sidebar.noControledDevice"));
diff --git a/src/components/setting/Basic.vue b/src/components/setting/Basic.vue
index 51024e7..8755e6c 100644
--- a/src/components/setting/Basic.vue
+++ b/src/components/setting/Basic.vue
@@ -86,7 +86,7 @@ function changeLanguage(language: "zh-CN" | "en-US") {
if (language === curLanguage.value) return;
curLanguage.value = language;
localStore.set("language", language);
- i18n.global.locale = language;
+ i18n.global.locale.value = language;
}
diff --git a/src/frontcommand/scrcpyMaskCmd.ts b/src/frontcommand/scrcpyMaskCmd.ts
index b74607e..fc9491b 100644
--- a/src/frontcommand/scrcpyMaskCmd.ts
+++ b/src/frontcommand/scrcpyMaskCmd.ts
@@ -35,7 +35,7 @@ export enum ScrcpyMaskCmdType {
type ScrcpyMaskCmdData = CmdDataSendKey | CmdDataTouch | CmdDataSwipe | String;
-enum SendKeyAction {
+export enum SendKeyAction {
Default = 0,
Down = 1,
Up = 2,
diff --git a/src/hotkey.ts b/src/hotkey.ts
index 135074a..7050a24 100644
--- a/src/hotkey.ts
+++ b/src/hotkey.ts
@@ -803,7 +803,7 @@ const loopDownKeyCBMap: Map Promise> = new Map();
const upKeyCBMap: Map Promise> = new Map();
const cancelAbleKeyList: string[] = [];
-function keydownHandler(event: KeyboardEvent) {
+function handleKeydown(event: KeyboardEvent) {
event.preventDefault();
if (event.repeat) return;
if (downKeyMap.has(event.code)) {
@@ -814,7 +814,7 @@ function keydownHandler(event: KeyboardEvent) {
}
}
-function keyupHandler(event: KeyboardEvent) {
+function handleKeyup(event: KeyboardEvent) {
event.preventDefault();
if (downKeyMap.has(event.code)) {
downKeyMap.set(event.code, false);
@@ -1209,8 +1209,8 @@ function applyKeyMappingConfigShortcuts(
}
export function listenToEvent() {
- window.addEventListener("keydown", keydownHandler);
- window.addEventListener("keyup", keyupHandler);
+ window.addEventListener("keydown", handleKeydown);
+ window.addEventListener("keyup", handleKeyup);
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
@@ -1220,8 +1220,8 @@ export function listenToEvent() {
}
export function unlistenToEvent() {
- window.removeEventListener("keydown", keydownHandler);
- window.removeEventListener("keyup", keyupHandler);
+ window.removeEventListener("keydown", handleKeydown);
+ window.removeEventListener("keyup", handleKeyup);
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
diff --git a/src/i18n/en-US.json b/src/i18n/en-US.json
index 9d5ffda..a3fcfa9 100644
--- a/src/i18n/en-US.json
+++ b/src/i18n/en-US.json
@@ -35,7 +35,14 @@
},
"controledDevice": "Controlled device",
"availableDevice": "Available devices",
- "noControledDevice": "No Controled Device"
+ "noControledDevice": "No Controled Device",
+ "alreadyControled": "Controlled device already exists",
+ "alreadyDisconnected": "Controlled device connection has been disconnected",
+ "externalControl": "External control",
+ "wsAddress": "Websocket address",
+ "inputWsAddress": "Please enter the Websocket address",
+ "wsClose": "Close",
+ "wsConnect": "Control"
},
"Mask": {
"inputBoxPlaceholder": "Input text and then press enter/esc",
@@ -216,5 +223,10 @@
},
"sidebar": {
"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"
}
}
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
index fc406d1..9428c85 100644
--- a/src/i18n/index.ts
+++ b/src/i18n/index.ts
@@ -8,6 +8,7 @@ const localStore = new Store("store.bin");
const i18n = createI18n({
allowComposition: true,
+ legacy: false,
messages: {
"en-US": enUS,
"zh-CN": zhCN,
@@ -15,7 +16,7 @@ const i18n = createI18n({
});
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;
diff --git a/src/i18n/zh-CN.json b/src/i18n/zh-CN.json
index 425e53c..1b4a199 100644
--- a/src/i18n/zh-CN.json
+++ b/src/i18n/zh-CN.json
@@ -35,7 +35,14 @@
},
"controledDevice": "受控设备",
"noControledDevice": "无受控设备",
- "availableDevice": "可用设备"
+ "availableDevice": "可用设备",
+ "alreadyControled": "已存在受控设备",
+ "alreadyDisconnected": "受控设备连接已断开",
+ "inputWsAddress": "请输入 Websocket 地址",
+ "externalControl": "外部控制",
+ "wsAddress": "Websocket 地址",
+ "wsClose": "断开",
+ "wsConnect": "控制"
},
"Mask": {
"keyconfigException": "按键方案异常,请删除此方案",
@@ -216,5 +223,10 @@
},
"sidebar": {
"noControledDevice": "未控制任何设备"
+ },
+ "websocket": {
+ "open": "已连接到外部控制服务端",
+ "close": "外部控制连接断开",
+ "error": "未知错误,外部控制连接断开"
}
}
diff --git a/src/invoke.ts b/src/invoke.ts
index 9c7f157..98f335c 100644
--- a/src/invoke.ts
+++ b/src/invoke.ts
@@ -29,6 +29,14 @@ export async function startScrcpyServer(
return await invoke("start_scrcpy_server", { id, scid, address });
}
+export async function getCurClientInfo(): Promise<{
+ device_name: string;
+ device_id: string;
+ scid: string;
+} | null> {
+ return await invoke("get_cur_client_info");
+}
+
export async function getDeviceScreenSize(
id: string
): Promise<[number, number]> {
diff --git a/src/store/global.ts b/src/store/global.ts
index 2ec0fae..73a83a9 100644
--- a/src/store/global.ts
+++ b/src/store/global.ts
@@ -1,6 +1,5 @@
import { defineStore } from "pinia";
import { Ref, ref } from "vue";
-import { Device } from "../invoke";
import {
KeyMapping,
KeyMappingConfig,
@@ -22,7 +21,7 @@ export const useGlobalStore = defineStore("global", () => {
interface ControledDevice {
scid: string;
deviceName: string;
- device: Device;
+ deviceID: string;
}
const controledDevice: Ref = ref(null);
@@ -70,6 +69,8 @@ export const useGlobalStore = defineStore("global", () => {
localStore.set("curKeyMappingIndex", index);
}
+ const externalControlled = ref(false);
+
// persistent storage
const screenSizeW: Ref = ref(0);
const screenSizeH: Ref = ref(0);
@@ -89,6 +90,7 @@ export const useGlobalStore = defineStore("global", () => {
curKeyMappingIndex,
maskButton,
checkUpdateAtStart,
+ externalControlled,
// in-memory storage
showLoading,
hideLoading,
diff --git a/src/websocket.ts b/src/websocket.ts
new file mode 100644
index 0000000..90a5bd2
--- /dev/null
+++ b/src/websocket.ts
@@ -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;
+let sharedStore: ReturnType;
+let t: ReturnType["t"];
+
+export function connectExternalControl(
+ url: string,
+ message: ReturnType,
+ store: ReturnType,
+ i18nT: ReturnType["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();
+}