mirror of
https://github.com/AkiChase/scrcpy-mask
synced 2025-02-22 14:42:15 +08:00
commit
a4da2d4ebe
@ -66,9 +66,8 @@
|
|||||||
2. 对于模拟器,不仅不需要投屏,而且模拟器通常默认启用 ADB 有线调试。所以几乎不用操作就能获得最好的体验。
|
2. 对于模拟器,不仅不需要投屏,而且模拟器通常默认启用 ADB 有线调试。所以几乎不用操作就能获得最好的体验。
|
||||||
3. 启动软件并导航到设备页面。
|
3. 启动软件并导航到设备页面。
|
||||||
1. 在可用的设备中查找你的设备(如果未找到,请自行搜索如何为安装设备启用 ADB 调试)。
|
1. 在可用的设备中查找你的设备(如果未找到,请自行搜索如何为安装设备启用 ADB 调试)。
|
||||||
2. 右击你的设备并选择“获取屏幕大小”。根据获得的屏幕尺寸为参考,正确输入设备的宽度和高度。注意:如果宽度或高度不正确 (例如,在纵向和横向模式下这两个参数是颠倒的),所有触摸操作将被忽略,但是不会有任何错误消息。
|
2. 右击设备并选择“控制此设备”。
|
||||||
3. 再次右击设备并选择“控制此设备”。
|
4. 导航到设置页面->蒙版设置,将蒙版的宽度和高度设置为设备屏幕尺寸相同的比例,确保蒙版大小合适。
|
||||||
4. 导航到设置页面->蒙版设置,将蒙版的宽度和高度设置为设备大小的一定倍数,以确保蒙版大小合适。
|
|
||||||
5. 导航到蒙版页面,你可以在其中看到一个完全透明的蒙版区域。接下来,调整并移动模拟器窗口或投屏窗口,让其内容区域与透明蒙版区域完全对齐。
|
5. 导航到蒙版页面,你可以在其中看到一个完全透明的蒙版区域。接下来,调整并移动模拟器窗口或投屏窗口,让其内容区域与透明蒙版区域完全对齐。
|
||||||
6. 导航到键映射页面,切换或编辑键映射配置。
|
6. 导航到键映射页面,切换或编辑键映射配置。
|
||||||
7. 返回到蒙版界面,开始使用吧!
|
7. 返回到蒙版界面,开始使用吧!
|
||||||
|
@ -68,9 +68,8 @@ Furthermore, to better support interaction between Scrcpy Mask and Android devic
|
|||||||
2. For emulator, you don't need screen mirror, and emulator generally default to enabling ADB wired debugging. So this is the best way for game, I think.
|
2. For emulator, you don't need screen mirror, and emulator generally default to enabling ADB wired debugging. So this is the best way for game, I think.
|
||||||
3. Launch the software and navigate to the Device page.
|
3. Launch the software and navigate to the Device page.
|
||||||
1. Find your device among the available devices (if not found, please search for how to enable ADB debugging for your device).
|
1. Find your device among the available devices (if not found, please search for how to enable ADB debugging for your device).
|
||||||
2. Right-click on your device and choose "Get Screen Size". Use the obtained screen size as a reference and enter the device's width and height correctly. Note: If the width or height is incorrect (for example, they are reversed in portrait and landscape modes), all touch operations will be ignored, but no error message will appear.
|
|
||||||
3. Right-click on your device again and choose "Control this device".
|
3. Right-click on your device again and choose "Control this device".
|
||||||
4. Navigate to the Settings page -> Mask Settings, and set the width and height of the mask to a certain multiple of the device's size to ensure the mask size is appropriate.
|
4. Navigate to the Settings page -> Mask Settings, set the width and height of the mask to the same ratio of the device screen size and ensure that the mask size is appropriate.
|
||||||
5. Navigate to the Mask page where you can see a transparent mask. Next, adjust and move your emulator window or screen mirroring window to align the displayed content area with the transparent mask area.
|
5. Navigate to the Mask page where you can see a transparent mask. Next, adjust and move your emulator window or screen mirroring window to align the displayed content area with the transparent mask area.
|
||||||
6. Navigate to the Key mapping page and switch or edit the key mapping configs.
|
6. Navigate to the Key mapping page and switch or edit the key mapping configs.
|
||||||
7. Return to the Mask page and start enjoying.
|
7. Return to the Mask page and start enjoying.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "scrcpy-mask",
|
"name": "scrcpy-mask",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.4.0",
|
"version": "0.4.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "scrcpy-mask"
|
name = "scrcpy-mask"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
description = "A Tauri App"
|
description = "A Tauri App"
|
||||||
authors = ["AkiChase"]
|
authors = ["AkiChase"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -8,6 +8,8 @@ use std::os::windows::process::CommandExt;
|
|||||||
|
|
||||||
use anyhow::{Context, Ok, Result};
|
use anyhow::{Context, Ok, Result};
|
||||||
|
|
||||||
|
use crate::share;
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Serialize)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@ -85,13 +87,14 @@ pub struct Adb;
|
|||||||
/// But some output of command won't be output, like adb service startup information.
|
/// But some output of command won't be output, like adb service startup information.
|
||||||
impl Adb {
|
impl Adb {
|
||||||
pub fn cmd_base() -> Command {
|
pub fn cmd_base() -> Command {
|
||||||
|
let adb_path = share::ADB_PATH.lock().unwrap().clone();
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
let mut cmd = Command::new("adb");
|
let mut cmd = Command::new(adb_path);
|
||||||
cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW
|
cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
Command::new("adb")
|
Command::new(adb_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// execute "adb devices" and return devices list
|
/// execute "adb devices" and return devices list
|
||||||
|
@ -157,6 +157,24 @@ fn check_adb_available() -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn set_adb_path(adb_path: String, app: tauri::AppHandle) -> Result<(), String> {
|
||||||
|
let app_h = app.app_handle().clone();
|
||||||
|
let stores = app_h.state::<tauri_plugin_store::StoreCollection<tauri::Wry>>();
|
||||||
|
let path = std::path::PathBuf::from("store.bin");
|
||||||
|
let store_res: Result<(), tauri_plugin_store::Error> =
|
||||||
|
tauri_plugin_store::with_store(app, stores, path, |store| {
|
||||||
|
store.insert("adbPath".to_string(), serde_json::json!(adb_path))?;
|
||||||
|
*share::ADB_PATH.lock().unwrap() = adb_path;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
match store_res {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
@ -168,8 +186,16 @@ async fn main() {
|
|||||||
let stores = app
|
let stores = app
|
||||||
.app_handle()
|
.app_handle()
|
||||||
.state::<tauri_plugin_store::StoreCollection<tauri::Wry>>();
|
.state::<tauri_plugin_store::StoreCollection<tauri::Wry>>();
|
||||||
let path = std::path::PathBuf::from("store.bin");
|
let path: std::path::PathBuf = std::path::PathBuf::from("store.bin");
|
||||||
tauri_plugin_store::with_store(app.app_handle().clone(), stores, path, |store| {
|
tauri_plugin_store::with_store(app.app_handle().clone(), stores, path, |store| {
|
||||||
|
// load adb path
|
||||||
|
match store.get("adbPath") {
|
||||||
|
Some(value) => *share::ADB_PATH.lock().unwrap() = value.to_string(),
|
||||||
|
None => store
|
||||||
|
.insert("adbPath".to_string(), serde_json::json!("adb"))
|
||||||
|
.unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
// restore window position and size
|
// restore window position and size
|
||||||
match store.get("maskArea") {
|
match store.get("maskArea") {
|
||||||
Some(value) => {
|
Some(value) => {
|
||||||
@ -239,7 +265,8 @@ async fn main() {
|
|||||||
get_device_screen_size,
|
get_device_screen_size,
|
||||||
adb_connect,
|
adb_connect,
|
||||||
load_default_keyconfig,
|
load_default_keyconfig,
|
||||||
check_adb_available
|
check_adb_available,
|
||||||
|
set_adb_path
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
@ -21,3 +21,7 @@ impl ClientInfo {
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref CLIENT_INFO: Mutex<Option<ClientInfo>> = Mutex::new(None);
|
pub static ref CLIENT_INFO: Mutex<Option<ClientInfo>> = Mutex::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref ADB_PATH: Mutex<String> = Mutex::new(String::from("adb"));
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"productName": "scrcpy-mask",
|
"productName": "scrcpy-mask",
|
||||||
"version": "0.4.0",
|
"version": "0.4.1",
|
||||||
"identifier": "com.akichase.mask",
|
"identifier": "com.akichase.mask",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "pnpm dev",
|
"beforeDevCommand": "pnpm dev",
|
||||||
|
@ -320,8 +320,13 @@ async function connectDevice() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
store.showLoading();
|
store.showLoading();
|
||||||
message.info(await adbConnect(wireless_address.value));
|
try {
|
||||||
await refreshDevices();
|
message.info(await adbConnect(wireless_address.value));
|
||||||
|
await refreshDevices();
|
||||||
|
} catch (e) {
|
||||||
|
message.error("t('pages.Device.adbConnectError')");
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectWS() {
|
function connectWS() {
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { h, nextTick, onActivated, onMounted, ref } from "vue";
|
import { h, nextTick, onActivated, onMounted, ref } from "vue";
|
||||||
import { NDialog, NInput, useDialog, useMessage } from "naive-ui";
|
import {
|
||||||
|
MessageReactive,
|
||||||
|
NDialog,
|
||||||
|
NInput,
|
||||||
|
useDialog,
|
||||||
|
useMessage,
|
||||||
|
} from "naive-ui";
|
||||||
import { useGlobalStore } from "../store/global";
|
import { useGlobalStore } from "../store/global";
|
||||||
import { onBeforeRouteLeave, useRouter } from "vue-router";
|
import { onBeforeRouteLeave, useRouter } from "vue-router";
|
||||||
import {
|
import {
|
||||||
@ -60,6 +66,7 @@ onActivated(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
store.checkAdb = checkAdb;
|
||||||
await checkAdb();
|
await checkAdb();
|
||||||
await loadLocalStore();
|
await loadLocalStore();
|
||||||
store.checkUpdate = checkUpdate;
|
store.checkUpdate = checkUpdate;
|
||||||
@ -67,11 +74,16 @@ onMounted(async () => {
|
|||||||
if (store.checkUpdateAtStart) checkUpdate();
|
if (store.checkUpdateAtStart) checkUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let checkAdbMessage: MessageReactive | null = null;
|
||||||
async function checkAdb() {
|
async function checkAdb() {
|
||||||
try {
|
try {
|
||||||
|
if (checkAdbMessage) {
|
||||||
|
checkAdbMessage.destroy();
|
||||||
|
checkAdbMessage = null;
|
||||||
|
}
|
||||||
await checkAdbAvailable();
|
await checkAdbAvailable();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
message.error(t("pages.Mask.checkAdb", [e]), {
|
checkAdbMessage = message.error(t("pages.Mask.checkAdb", [e]), {
|
||||||
duration: 0,
|
duration: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,20 @@ import {
|
|||||||
NCard,
|
NCard,
|
||||||
NIcon,
|
NIcon,
|
||||||
NSelect,
|
NSelect,
|
||||||
|
NInputGroup,
|
||||||
|
useMessage,
|
||||||
} from "naive-ui";
|
} from "naive-ui";
|
||||||
import { relaunch } from "@tauri-apps/plugin-process";
|
import { relaunch } from "@tauri-apps/plugin-process";
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
import i18n from "../../i18n";
|
import i18n from "../../i18n";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { setAdbPath } from "../../invoke";
|
||||||
|
import { useGlobalStore } from "../../store/global";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const localStore = new Store("store.bin");
|
const localStore = new Store("store.bin");
|
||||||
|
const store = useGlobalStore();
|
||||||
|
const message = useMessage();
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
|
|
||||||
const localStoreEntries = ref<[string, unknown][]>([]);
|
const localStoreEntries = ref<[string, unknown][]>([]);
|
||||||
@ -36,9 +42,12 @@ const languageOptions = [
|
|||||||
|
|
||||||
const curLanguage = ref("en-US");
|
const curLanguage = ref("en-US");
|
||||||
|
|
||||||
|
const adbPath = ref("");
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
refreshLocalData();
|
refreshLocalData();
|
||||||
curLanguage.value = (await localStore.get<string>("language")) ?? "en-US";
|
curLanguage.value = (await localStore.get<string>("language")) ?? "en-US";
|
||||||
|
adbPath.value = (await localStore.get<string>("adbPath")) ?? "";
|
||||||
});
|
});
|
||||||
|
|
||||||
async function refreshLocalData() {
|
async function refreshLocalData() {
|
||||||
@ -88,6 +97,15 @@ function changeLanguage(language: "zh-CN" | "en-US") {
|
|||||||
localStore.set("language", language);
|
localStore.set("language", language);
|
||||||
i18n.global.locale.value = language;
|
i18n.global.locale.value = language;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function adjustAdbPath() {
|
||||||
|
store.showLoading();
|
||||||
|
await setAdbPath(adbPath.value);
|
||||||
|
message.success(t("pages.Setting.Basic.adbPath.setSuccess"));
|
||||||
|
await store.checkAdb();
|
||||||
|
adbPath.value = (await localStore.get<string>("adbPath")) ?? "";
|
||||||
|
store.hideLoading();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -99,6 +117,17 @@ function changeLanguage(language: "zh-CN" | "en-US") {
|
|||||||
:options="languageOptions"
|
:options="languageOptions"
|
||||||
style="max-width: 300px; margin: 20px 0"
|
style="max-width: 300px; margin: 20px 0"
|
||||||
/>
|
/>
|
||||||
|
<NH4 prefix="bar">{{ $t("pages.Setting.Basic.adbPath.title") }}</NH4>
|
||||||
|
<NInputGroup style="max-width: 300px; margin-bottom: 20px">
|
||||||
|
<NInput
|
||||||
|
v-model:value="adbPath"
|
||||||
|
clearable
|
||||||
|
:placeholder="$t('pages.Setting.Basic.adbPath.placeholder')"
|
||||||
|
/>
|
||||||
|
<NButton type="primary" @click="adjustAdbPath">{{
|
||||||
|
$t("pages.Setting.Basic.adbPath.set")
|
||||||
|
}}</NButton>
|
||||||
|
</NInputGroup>
|
||||||
<NFlex justify="space-between">
|
<NFlex justify="space-between">
|
||||||
<NH4 prefix="bar">{{ $t("pages.Setting.Basic.localStore") }}</NH4>
|
<NH4 prefix="bar">{{ $t("pages.Setting.Basic.localStore") }}</NH4>
|
||||||
<NFlex>
|
<NFlex>
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"inputWsAddress": "Please enter the Websocket address",
|
"inputWsAddress": "Please enter the Websocket address",
|
||||||
"wsClose": "Close",
|
"wsClose": "Close",
|
||||||
"wsConnect": "Control",
|
"wsConnect": "Control",
|
||||||
"adbDeviceError": "Unable to get available devices"
|
"adbDeviceError": "Unable to get available devices",
|
||||||
|
"adbConnectError": "Wireless connection failed"
|
||||||
},
|
},
|
||||||
"Mask": {
|
"Mask": {
|
||||||
"inputBoxPlaceholder": "Input text and then press enter/esc",
|
"inputBoxPlaceholder": "Input text and then press enter/esc",
|
||||||
@ -100,7 +101,13 @@
|
|||||||
},
|
},
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"localStore": "Local data",
|
"localStore": "Local data",
|
||||||
"delCurData": "Delete current data"
|
"delCurData": "Delete current data",
|
||||||
|
"adbPath": {
|
||||||
|
"placeholder": "adb path",
|
||||||
|
"set": "Save",
|
||||||
|
"setSuccess": "adb path set successfully",
|
||||||
|
"title": "adb path"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"About": {
|
"About": {
|
||||||
"introduction": "A Scrcpy client in Rust & Tarui aimed at providing mouse and key mapping to control Android device.",
|
"introduction": "A Scrcpy client in Rust & Tarui aimed at providing mouse and key mapping to control Android device.",
|
||||||
|
@ -35,7 +35,8 @@
|
|||||||
"wsAddress": "Websocket 地址",
|
"wsAddress": "Websocket 地址",
|
||||||
"wsClose": "断开",
|
"wsClose": "断开",
|
||||||
"wsConnect": "控制",
|
"wsConnect": "控制",
|
||||||
"adbDeviceError": "无法获取可用设备"
|
"adbDeviceError": "无法获取可用设备",
|
||||||
|
"adbConnectError": "无线连接失败"
|
||||||
},
|
},
|
||||||
"Mask": {
|
"Mask": {
|
||||||
"keyconfigException": "按键方案异常,请删除此方案",
|
"keyconfigException": "按键方案异常,请删除此方案",
|
||||||
@ -100,7 +101,13 @@
|
|||||||
},
|
},
|
||||||
"language": "语言",
|
"language": "语言",
|
||||||
"localStore": "本地数据",
|
"localStore": "本地数据",
|
||||||
"delCurData": "删除当前数据"
|
"delCurData": "删除当前数据",
|
||||||
|
"adbPath": {
|
||||||
|
"setSuccess": "adb 路径设置成功",
|
||||||
|
"title": "adb 路径",
|
||||||
|
"placeholder": "adb 路径",
|
||||||
|
"set": "设置"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"About": {
|
"About": {
|
||||||
"about": "关于",
|
"about": "关于",
|
||||||
|
@ -51,8 +51,12 @@ export async function loadDefaultKeyconfig(): Promise<string> {
|
|||||||
return await invoke("load_default_keyconfig");
|
return await invoke("load_default_keyconfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkAdbAvailable(): Promise<void>{
|
export async function checkAdbAvailable(): Promise<void> {
|
||||||
return await invoke("check_adb_available");
|
return await invoke("check_adb_available");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setAdbPath(path: string): Promise<string> {
|
||||||
|
return await invoke("set_adb_path", { adbPath: path });
|
||||||
|
}
|
||||||
|
|
||||||
export type { Device };
|
export type { Device };
|
||||||
|
@ -30,6 +30,7 @@ export const useGlobalStore = defineStore("global", () => {
|
|||||||
const showInputBox: (_: boolean) => void = (_: boolean) => {};
|
const showInputBox: (_: boolean) => void = (_: boolean) => {};
|
||||||
|
|
||||||
let checkUpdate: () => Promise<void> = async () => {};
|
let checkUpdate: () => Promise<void> = async () => {};
|
||||||
|
let checkAdb: () => Promise<void> = async () => {};
|
||||||
|
|
||||||
function applyEditKeyMappingList(): boolean {
|
function applyEditKeyMappingList(): boolean {
|
||||||
const set = new Set<string>();
|
const set = new Set<string>();
|
||||||
@ -103,5 +104,6 @@ export const useGlobalStore = defineStore("global", () => {
|
|||||||
resetEditKeyMappingList,
|
resetEditKeyMappingList,
|
||||||
setKeyMappingIndex,
|
setKeyMappingIndex,
|
||||||
checkUpdate,
|
checkUpdate,
|
||||||
|
checkAdb,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user