mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-26 05:39:45 +08:00
270 lines
7.3 KiB
Rust
270 lines
7.3 KiB
Rust
// 命令模块,负责处理命令
|
||
// @since Beta v0.9.1
|
||
|
||
use crate::utils;
|
||
use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder};
|
||
use tauri_utils::config::{WebviewUrl, WindowConfig};
|
||
|
||
// 放一个常数,用来判断应用是否初始化
|
||
static mut APP_INITIALIZED: bool = false;
|
||
|
||
// 初始化应用
|
||
#[tauri::command]
|
||
pub async fn init_app(app_handle: AppHandle) {
|
||
unsafe {
|
||
if APP_INITIALIZED == true {
|
||
return;
|
||
}
|
||
}
|
||
app_handle.emit("initApp", ()).unwrap();
|
||
unsafe {
|
||
APP_INITIALIZED = true;
|
||
}
|
||
}
|
||
|
||
// 创建窗口
|
||
#[tauri::command]
|
||
pub async fn create_window(
|
||
app_handle: AppHandle,
|
||
label: String,
|
||
url: String,
|
||
option: WindowConfig,
|
||
) {
|
||
let window_find = app_handle.get_webview_window(&label);
|
||
if window_find.is_some() {
|
||
window_find.unwrap().destroy().unwrap();
|
||
return;
|
||
}
|
||
let url_parse = WebviewUrl::App(url.parse().unwrap());
|
||
WebviewWindowBuilder::new(&app_handle, &label, url_parse)
|
||
.inner_size(option.width, option.height)
|
||
.resizable(option.resizable)
|
||
.visible(option.visible)
|
||
.title(option.title)
|
||
.center()
|
||
.additional_browser_args("--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --autoplay-policy=no-user-gesture-required")
|
||
.build()
|
||
.unwrap();
|
||
}
|
||
|
||
// 执行 js
|
||
#[tauri::command]
|
||
pub async fn execute_js(app_handle: AppHandle, label: String, js: String) {
|
||
let window = app_handle.get_webview_window(&label);
|
||
if window.is_some() {
|
||
window.unwrap().eval(js).unwrap();
|
||
}
|
||
}
|
||
|
||
// 获取目录大小
|
||
#[tauri::command]
|
||
pub async fn get_dir_size(path: String) -> u64 {
|
||
let walk_dir = walkdir::WalkDir::new(path);
|
||
let mut size = 0;
|
||
for entry in walk_dir {
|
||
let entry = entry.unwrap();
|
||
let file_type = entry.file_type();
|
||
if file_type.is_file() {
|
||
size += entry.metadata().unwrap().len();
|
||
}
|
||
}
|
||
size
|
||
}
|
||
|
||
// 判断是否是管理员权限
|
||
#[tauri::command]
|
||
pub fn is_in_admin() -> bool {
|
||
#[cfg(not(target_os = "windows"))]
|
||
{
|
||
return false;
|
||
}
|
||
#[cfg(target_os = "windows")]
|
||
{
|
||
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
|
||
use windows_sys::Win32::Security::{
|
||
GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation,
|
||
};
|
||
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
|
||
|
||
unsafe {
|
||
let mut token_handle: HANDLE = std::ptr::null_mut();
|
||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle) == 0 {
|
||
return false;
|
||
}
|
||
|
||
let mut elevation = TOKEN_ELEVATION { TokenIsElevated: 0 };
|
||
let mut return_length = 0;
|
||
|
||
let result = GetTokenInformation(
|
||
token_handle,
|
||
TokenElevation,
|
||
&mut elevation as *mut _ as *mut _,
|
||
std::mem::size_of::<TOKEN_ELEVATION>() as u32,
|
||
&mut return_length,
|
||
);
|
||
|
||
CloseHandle(token_handle);
|
||
|
||
result != 0 && elevation.TokenIsElevated != 0
|
||
}
|
||
}
|
||
}
|
||
|
||
// 隐藏主窗口到托盘
|
||
#[tauri::command]
|
||
pub async fn hide_main_window(app_handle: AppHandle) {
|
||
// 关闭所有子窗口
|
||
for label in crate::SUB_WINDOW_LABELS.iter() {
|
||
if let Some(sub) = app_handle.get_webview_window(label) {
|
||
let _ = sub.destroy();
|
||
}
|
||
}
|
||
// 隐藏主窗口
|
||
if let Some(window) = app_handle.get_webview_window("TeyvatGuide") {
|
||
let _ = window.hide();
|
||
}
|
||
}
|
||
|
||
// 退出应用
|
||
#[tauri::command]
|
||
pub async fn quit_app(app_handle: AppHandle) {
|
||
// 关闭所有子窗口
|
||
for label in crate::SUB_WINDOW_LABELS.iter() {
|
||
if let Some(sub) = app_handle.get_webview_window(label) {
|
||
let _ = sub.destroy();
|
||
}
|
||
}
|
||
// 退出应用
|
||
app_handle.exit(0);
|
||
}
|
||
|
||
/// 获取当前系统的文本缩放比例(TextScaleFactor)
|
||
/// 返回值示例:1.0 表示 100%,1.25 表示 125%
|
||
#[tauri::command]
|
||
pub fn read_text_scale() -> Result<f64, String> {
|
||
utils::read_text_scale_factor()
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn launch_game(path: String, ticket: String) -> Result<(), String> {
|
||
#[cfg(target_os = "windows")]
|
||
{
|
||
// 依赖 widestring 和 windows-sys
|
||
use widestring::U16CString;
|
||
use windows_sys::Win32::Foundation::HWND;
|
||
use windows_sys::Win32::UI::Shell::ShellExecuteW;
|
||
use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
|
||
|
||
// 构造参数字符串
|
||
let args = format!("login_auth_ticket={}", ticket);
|
||
|
||
// 转为 UTF-16 C 字符串
|
||
let operation =
|
||
U16CString::from_str("runas").map_err(|e| format!("encode operation error: {}", e))?;
|
||
let file = U16CString::from_str(&path).map_err(|e| format!("encode path error: {}", e))?;
|
||
let params = U16CString::from_str(&args).map_err(|e| format!("encode params error: {}", e))?;
|
||
|
||
// 调用 ShellExecuteW
|
||
unsafe {
|
||
let res = ShellExecuteW(
|
||
0 as HWND,
|
||
operation.as_ptr(),
|
||
file.as_ptr(),
|
||
params.as_ptr(),
|
||
std::ptr::null(),
|
||
SW_SHOWNORMAL,
|
||
);
|
||
let code = res as isize;
|
||
if code <= 32 {
|
||
return Err(format!("ShellExecuteW failed, code: {}.", code));
|
||
}
|
||
}
|
||
Ok(())
|
||
}
|
||
#[cfg(not(target_os = "windows"))]
|
||
{
|
||
Err("This command is only supported on Windows".into())
|
||
}
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn is_msix() -> bool {
|
||
#[cfg(not(windows))]
|
||
{
|
||
false
|
||
}
|
||
#[cfg(windows)]
|
||
{
|
||
use std::ptr;
|
||
use widestring::U16CStr;
|
||
use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
|
||
use windows_sys::Win32::Storage::Packaging::Appx::GetCurrentPackageFullName;
|
||
unsafe {
|
||
let mut length: u32 = 0;
|
||
let result = GetCurrentPackageFullName(&mut length, ptr::null_mut());
|
||
if result != ERROR_INSUFFICIENT_BUFFER {
|
||
println!("Not running in MSIX package. Error code: {}", result);
|
||
return false;
|
||
}
|
||
let mut buffer = vec![0u16; length as usize];
|
||
let result = GetCurrentPackageFullName(&mut length, buffer.as_mut_ptr());
|
||
if result != 0 {
|
||
println!("Failed to retrieve package full name. Error code: {}", result);
|
||
return false;
|
||
}
|
||
let pkg_name = U16CStr::from_ptr_str(buffer.as_ptr());
|
||
println!("MSIX Package Full Name: {}", pkg_name.to_string_lossy());
|
||
true
|
||
}
|
||
}
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn is_process_running(process_name: String) -> bool {
|
||
#[cfg(not(target_os = "windows"))]
|
||
{
|
||
false
|
||
}
|
||
#[cfg(target_os = "windows")]
|
||
{
|
||
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE};
|
||
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
|
||
CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW,
|
||
TH32CS_SNAPPROCESS,
|
||
};
|
||
unsafe {
|
||
// 创建进程快照
|
||
let snapshot: HANDLE = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||
if snapshot == INVALID_HANDLE_VALUE {
|
||
return false;
|
||
}
|
||
|
||
let mut entry: PROCESSENTRY32W = std::mem::zeroed();
|
||
entry.dwSize = std::mem::size_of::<PROCESSENTRY32W>() as u32;
|
||
|
||
// 遍历进程列表
|
||
if Process32FirstW(snapshot, &mut entry) != 0 {
|
||
loop {
|
||
// 将 exe 文件名转为 Rust String
|
||
let exe_name = {
|
||
let len = entry.szExeFile.iter().position(|&c| c == 0).unwrap_or(entry.szExeFile.len());
|
||
String::from_utf16_lossy(&entry.szExeFile[..len])
|
||
};
|
||
|
||
if exe_name.eq_ignore_ascii_case(&process_name) {
|
||
CloseHandle(snapshot);
|
||
return true;
|
||
}
|
||
|
||
if Process32NextW(snapshot, &mut entry) == 0 {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
CloseHandle(snapshot);
|
||
false
|
||
}
|
||
}
|
||
}
|